Throughput vs Latency Trade-offs in Kafka
Every Kafka team wants two things at once: move enormous amounts of data, and move each piece of it instantly. Kafka lets you have a great deal of both, but never the maximum of both together. This guide explains why, and how to choose wisely.
Picture a city bus and a taxi. A bus carries fifty people at once, so it moves a huge number of passengers per hour, but it waits at stops and takes a fixed route, so any single passenger may wait a while. A taxi carries one person straight to the door with almost no waiting, but it moves far fewer people per hour. Neither is better. They are tuned for different goals, and a transport planner must decide how many of each the city needs.
Kafka faces the same choice every second. It can gather many messages into one big package and send them together, which is efficient but means the first message in the package waits for the others. Or it can send each message the moment it appears, which is fast for that message but wasteful, because every trip carries a lot of overhead. The settings you choose decide where your system sits between the bus and the taxi.
In this guide we go step by step, in plain language. We start with what the two words mean and how they are measured, follow a message through Kafka to see where time goes, learn the settings that shift the balance, and finish with tuning profiles, monitoring, security, mistakes and real-world stories from well-known companies.
1Two Words That Pull Apart
Before tuning anything, we need clear meanings for the two ideas that everything else depends on.
Throughput: how much
Throughput is the amount of work completed in a period of time. In Kafka it is usually measured as messages per second or megabytes per second. A cluster that accepts one million messages every second has high throughput. Throughput answers the question: “How much data can this system handle?”
Latency: how long
Latency is the time one piece of work takes from start to finish. In Kafka it is usually measured in milliseconds. It might be the time from a producer sending a message to the broker confirming it, or the time from the message being created to a consumer receiving it. Latency answers the question: “How quickly does one message get through?”
Think of a water pipe. Throughput is how many litres flow through per minute, which depends on how wide the pipe is. Latency is how long a single drop takes to travel from the tap to the end of the pipe, which depends on how long the pipe is. A wide pipe can carry a lot even if it is long, and a short thin pipe delivers each drop quickly but carries little. The two numbers describe different things.
Why they are not the same thing
It is tempting to think a fast system must be fast in both ways. In practice they are separate. A system can move a huge volume of data while each message waits a long time in queues and batches. Another system can respond to each request instantly, yet collapse when the load doubles. A mail truck that leaves once a day carrying a million letters has enormous throughput and terrible latency. A courier on a bicycle has low throughput and excellent latency.
Bulk movement
Log collection, clickstream analytics, data lake loading. Nobody cares if a record arrives a few seconds late, but the total volume is huge.
Instant reactions
Fraud checks, live notifications, trading signals. Each event must be handled within milliseconds, even if volume is modest.
Most business systems
Order processing, payments and inventory updates need decent speed and decent volume, and accept a moderate compromise.
Always begin a tuning conversation by asking what the business needs. “Make it faster” is not a goal. “Ninety-nine percent of orders must reach the warehouse system within 200 milliseconds while we handle 50,000 orders a minute” is.
2How to Measure Them Honestly
A trade-off can only be managed if you measure both sides properly. Averages alone can mislead badly.
Measuring throughput
Throughput is measured over a period, and it helps to record it in two ways: records per second and bytes per second. A million tiny records and a thousand huge records may have the same byte rate but stress the system differently. Small records make the cluster spend more effort per byte on bookkeeping. Large records make network and disk the limits.
Measuring latency with percentiles
Latency varies from message to message, so a single number hides the story. Engineers use percentiles. The 50th percentile, called the median, is the time that half of requests beat. The 99th percentile is the time that ninety-nine percent of requests beat, so it describes the slowest one in a hundred.
| Measure | What it tells you | Why it matters |
|---|---|---|
| Average | The mean of all times | Easy to compute but hides slow outliers |
| 50th percentile (median) | The typical experience | Shows normal behaviour |
| 95th percentile | What one request in twenty suffers | Shows early signs of stress |
| 99th percentile | What one request in a hundred suffers | Often the number that customers notice |
| 99.9th percentile | The rare worst cases | Important where many steps are chained together |
| Maximum | The single slowest request | Useful for spotting pauses and outages |
A pizza shop says its average delivery time is twenty minutes. That sounds fine, but if ninety orders arrive in ten minutes and ten arrive after an hour, the average hides ten unhappy families. The slowest orders are what people complain about, so shops that care look at the slow end, not the middle.
The tail matters more than it seems
Modern applications often call many services in a chain. If each step has a one percent chance of being slow, a request that passes through fifty steps has a large chance of meeting at least one slow step. This is why large companies pay attention to the 99th and 99.9th percentiles. Rare slowness at each layer becomes common slowness for the whole request.
Different kinds of latency in Kafka
Send latency
Time from the application handing over a message until the broker confirms it was stored to the level you asked for.
Request latency
Time the broker spends handling one request: waiting in a queue, writing to disk, waiting for copies and sending the reply.
Fetch latency
Time for a consumer’s read request to be answered, including any deliberate waiting for more data.
Message age
Time from the message being created to the consumer finishing with it. This is what users actually feel.
Producer latency, the time until an acknowledgement, is not the same as end-to-end latency. A message can be safely stored in milliseconds and still be read a minute later because the consumer is busy or behind. Measure both.
3The Journey of One Message
To tune Kafka you need to know where a message spends its time. Follow one message from creation to consumption.
flowchart LR
A["Application creates a message"] --> B["Producer buffer: waits to join a batch"]
B --> C["Network to the partition leader"]
C --> D["Leader appends to its log"]
D --> E["Followers copy the batch"]
E --> F["Leader acknowledges the producer"]
D --> G["Consumer fetch reads the message"]
G --> H["Application processes the message"]
Reading the diagram
The message is born in your application, which hands it to the Kafka producer library. The producer does not usually send it immediately. It places the message in a buffer, grouped by destination partition, and waits until a batch is ready. The batch then travels over the network to the leader, the broker that owns that partition. The leader appends the batch to the end of its log, which is a file on disk. Other brokers holding copies, called followers, fetch the batch and store it too. Once enough copies exist, as required by your settings, the leader sends an acknowledgement to the producer.
Consumers work separately. A consumer asks the leader for data after its last position. The leader answers with whatever is available, possibly after waiting for a minimum amount to accumulate. The consumer then processes the messages and saves its position.
Sending a parcel involves packing at home, a wait for the courier’s pickup round, a trip to the depot, sorting, a trip to the recipient’s city and delivery. Each stage adds a little time, and each can be shortened, though usually at a price. Kafka’s stages are similar, and tuning means deciding which waits are worth paying.
Where the time goes
| Stage | Typical cause of delay | What influences it |
|---|---|---|
| Producer buffer | Waiting for a batch to fill | Linger time, batch size, message rate |
| Compression | Processor time to shrink data | Codec choice, batch size |
| Network | Distance, bandwidth, TLS | Same-zone placement, encryption, message size |
| Broker request queue | Busy broker threads | Thread counts, overall load |
| Disk write | Operating system cache and flushing | Disk type, flush settings |
| Replication | Waiting for followers | Acknowledgement level, follower speed |
| Consumer fetch | Waiting for minimum data | Minimum fetch bytes, maximum wait |
| Processing | Application work per message | Your code, batch sizes, downstream systems |
Many teams discover that the Kafka brokers are not the slowest stage at all. Slow downstream databases and long processing steps inside consumers often dominate end-to-end latency. Measure before you tune.
4Batching: The Heart of the Trade-off
If you remember one idea from this guide, remember batching. Almost every Kafka throughput gain comes from it, and almost every latency cost comes with it.
What batching means
Instead of sending each message alone, the producer collects several messages destined for the same partition and sends them as one batch. The batch has one header, one network request and one disk write, no matter how many messages it holds. The fixed costs are shared among all of them.
Posting letters one at a time means walking to the postbox for each one. Collecting them in a tray and walking once a day means one trip covers dozens. The daily trip is far more efficient, but the first letter written in the morning waits until evening. The efficiency and the delay are two faces of the same decision.
sequenceDiagram
participant App as Application
participant Buf as Producer buffer
participant Br as Broker
App->>Buf: message 1
App->>Buf: message 2
App->>Buf: message 3
Note over Buf: Waits until the batch is full or the linger time ends
Buf->>Br: one request with three messages
Br-->>Buf: one acknowledgement
Why batching improves throughput
- Fewer requests: the broker handles one request instead of many, so its threads and queues are less crowded.
- Less overhead per message: headers, checksums and network framing are paid once per batch.
- Better compression: compressing many similar messages together shrinks them far more than compressing each one alone.
- Sequential disk writes: larger appends make the disk and operating system work smoothly.
Why batching adds latency
The first message in a batch must wait for the batch to fill or for a timer to expire. When traffic is heavy, batches fill in a moment and the cost is tiny. When traffic is light, a batch may never fill, so the timer decides how long each message waits. This is why the same setting can be harmless on a busy system and painful on a quiet one.
Batching happens per partition. If you spread a low message rate over hundreds of partitions, each partition receives only a trickle, and batches stay tiny. More partitions can quietly reduce batching efficiency.
5The Producer Dials
A handful of producer settings control most of the balance. Think of them as dials on a mixing desk.
| Setting | What it does | Turn up for | Turn down for |
|---|---|---|---|
| linger.ms | How long the producer waits for more messages before sending a batch | Throughput | Latency |
| batch.size | Maximum bytes in one batch per partition | Throughput | Memory savings |
| compression.type | Codec used to shrink batches | Network and storage savings | Lowest processor cost |
| buffer.memory | Total memory for waiting messages | Absorbing bursts | Small memory footprint |
| max.in.flight.requests.per.connection | How many unanswered requests may be outstanding per broker | Throughput | Strict simplicity |
| acks | How many copies must confirm a write | Lower latency at the cost of safety | Durability |
| send.buffer.bytes and receive.buffer.bytes | Network socket buffers | Long, fast links | Small memory use |
Linger time: the timer
The linger setting says: “if the batch is not full yet, wait this many milliseconds for more messages before sending.” A value of zero means send as soon as possible, though messages that arrive while a request is in flight still end up batched together naturally. A value of five to twenty milliseconds is a common throughput-friendly choice, adding a small, bounded delay in return for much bigger batches. The exact behaviour of the default has changed between client versions, so check the documentation for the version you run.
Batch size: the container
The batch size is the size of the container, in bytes, per partition. If messages arrive quickly, the container fills and is sent without waiting for the timer. A larger container means more messages per request and better compression, but more memory used per partition. If the container is much larger than what fills within your linger time, the extra size is never used.
A shuttle van leaves either when it is full or when its departure time arrives, whichever comes first. Linger time is the departure time, and batch size is the number of seats. On a busy day the van fills and leaves early. On a quiet day it leaves on schedule, half empty. A bigger van helps only if there are enough passengers to fill it within the waiting time.
Buffer memory and back-pressure
The producer holds unsent messages in a memory buffer. If the application produces faster than the brokers accept, the buffer fills. When it is full, further send calls wait, up to a limit, and then fail. This back-pressure protects the application from running out of memory, but it shows up as sudden latency for the application thread. A larger buffer absorbs bursts but hides slowness longer.
In-flight requests
A producer can send several batches to a broker without waiting for each reply. Allowing more outstanding requests keeps the pipe full and raises throughput, especially over long distances. With idempotence enabled, Kafka can allow up to five outstanding requests while still preserving order within a partition, which is why the modern defaults are friendly to both speed and safety.
Change one dial at a time and measure. Changing linger, batch size and compression together makes it impossible to know which one helped.
How the dials interact
The dials never act alone. A long linger time only helps if the batch size is large enough to hold what accumulates. A large batch size only helps if traffic is heavy enough to fill it. Compression works better with bigger batches, but it also takes processor time that a busy application server may not have to spare. Acknowledgement of all lengthens each round trip, so allowing more requests in flight keeps the pipe full while waiting. Understanding these links is what separates thoughtful tuning from random guessing.
A useful way to think about it is to ask two questions for every application. First: how long may a message wait before it must leave the producer? That answer sets linger time. Second: how much data typically arrives during that wait? That answer sets a sensible batch size. Once those two are matched, compression and buffer memory can be sized around them.
6Compression: Trading Processor Time for Space
Compression shrinks batches so they use less network, less disk and less time on the wire. It costs some processor time, so it is a trade-off of its own.
Why compression often raises throughput
Networks and disks have limits measured in bytes per second. If a batch shrinks to a third of its size, three times as many messages fit through the same pipe. On many clusters the network or disk is the bottleneck, not the processor, so spending a little processor time to save a lot of bandwidth is a good exchange. Compressed batches also reduce replication traffic between brokers and the disk space needed.
Vacuum-packing clothes takes a minute, but the suitcase then holds twice as much and the trolley is lighter. If you are limited by suitcase space, the minute is well spent. If you are in a hurry and have plenty of space, skipping the packing is quicker. Compression is vacuum-packing for messages.
The common codecs
| Codec | Speed | Space saving | Best when |
|---|---|---|---|
| None | No extra work | None | Data is already compressed, or latency matters above all |
| LZ4 | Very fast | Good | A safe general choice for balance |
| Snappy | Very fast | Good | Similar to LZ4, widely used for years |
| Zstandard | Fast to moderate | Very good | Bandwidth or storage is costly and some processor time can be spared |
| Gzip | Slower | Good to very good | Older setups and compatibility, when speed matters less |
How compression interacts with batching
Compression works on whole batches. A larger batch of similar messages compresses better than a small one, so linger time and batch size influence compression ratios. Text-like data such as JSON or logs compresses well. Data that is already compressed, such as images or encrypted payloads, barely shrinks and wastes processor time.
Producers compress, brokers usually store the batch as received, and consumers decompress. This means the producer machines and consumer machines pay the processor cost, while brokers benefit from smaller data. Brokers can be forced to recompress if the topic’s setting differs from the producer’s, which costs broker processor time, so keep them consistent.
Benefits of compression
- Higher throughput when the network or disk is the limit.
- Lower storage and cloud data transfer costs.
- Less replication traffic between brokers.
- Often lower end-to-end latency on slow links, because fewer bytes travel.
Costs of compression
- Extra processor use on producers and consumers.
- A small delay to compress each batch.
- Little benefit for already compressed data.
- Recompression on brokers if settings are inconsistent.
Check the compression ratio metric on producers. A ratio close to one means compression is wasting effort on your data, and a ratio of 0.3 means you are saving about seventy percent of the bytes.
7Acknowledgements and Replication: Safety Costs Time
The most important latency decision in Kafka is not about speed at all. It is about how many copies must confirm a message before the producer is told “done”.
Copies and acknowledgements
Kafka keeps several copies of each partition on different brokers. The number of copies is the replication factor, commonly three. The producer’s acks setting says how many of them must confirm a write before the leader replies.
| Setting | Meaning | Latency | Safety |
|---|---|---|---|
| acks = 0 | Do not wait for any reply | Lowest | Messages can be lost without anyone knowing |
| acks = 1 | Wait for the leader only | Low | Lost if the leader fails before followers copy the message |
| acks = all | Wait for every in-sync replica | Higher | Survives the loss of the leader |
sequenceDiagram
participant P as Producer
participant L as Leader broker
participant F1 as Follower 1
participant F2 as Follower 2
P->>L: batch of messages
L->>L: append to log
F1->>L: fetch new data
F2->>L: fetch new data
L-->>P: acknowledgement once required copies exist
A bank clerk can say “your transfer is done” the moment they type it in, or only after the head office, the backup office and the archive have all confirmed. The first answer is instant but risky if the clerk’s computer breaks. The second answer takes a little longer but is safe against any single failure. Kafka lets you choose which answer your application receives.
The minimum in-sync replicas rule
The in-sync replicas are the copies that are keeping up with the leader. The min.insync.replicas setting says how many of them must exist for a write with acknowledgement of all to be accepted. A common safe setup is replication factor three with a minimum of two, so that one broker can fail without stopping writes or losing acknowledged data. If too few copies are in step, writes are refused rather than accepted unsafely.
What replication actually costs in time
With acknowledgement of all, send latency includes the time for followers to fetch the batch and the leader to see that they have it. If followers are in the same data center, this may be a millisecond or two. If they are in different availability zones, a few milliseconds more. If one follower is slow, the whole acknowledgement waits for it or until it is removed from the in-sync set. This is why slow disks or busy machines on a single broker can raise latency for many partitions at once.
Choosing acknowledgement of zero or one to shave latency is a decision about data loss, not merely speed. Make it deliberately, for data you can afford to lose, and record the decision so future engineers understand it.
Stronger acknowledgement
- Acknowledged data survives a broker failure.
- Fewer surprises during rolling restarts.
- Simpler reasoning about correctness.
Weaker acknowledgement
- Lower send latency.
- Slightly higher raw throughput per producer.
- Risk of silent data loss during failures.
Choosing by the value of the data
A practical way to decide the acknowledgement level is to ask what losing one message would cost. A lost page-view event in a clickstream may be statistically invisible, while a lost payment instruction is a serious incident. Many organizations therefore classify topics by importance and apply strict durability to the critical ones while relaxing it only where loss is genuinely acceptable. The classification, not the speed graph, should drive the decision.
8Why Kafka Brokers Are Fast
Kafka achieves remarkable throughput on ordinary hardware by leaning on how operating systems and disks like to work.
Sequential writes
Kafka appends every batch to the end of a log file. Writing sequentially is far faster than jumping around a disk, even on old spinning disks, and it is comfortable for solid-state drives too. There is no complicated rearranging of data on write. Reading is also sequential for consumers that follow the log.
Writing entries one after another in a diary is quick. Filing each entry into a different folder in a different cabinet is slow. Kafka’s log is a diary, and this simple choice is a big reason it is fast.
The operating system’s page cache
Kafka does not keep its own large cache of messages. It relies on the operating system’s page cache, spare memory that holds recently used file data. A message just written is usually still in memory when consumers ask for it, so reads are served from memory rather than disk. This is why leaving plenty of free memory for the operating system, instead of giving everything to the Java heap, is a well-known Kafka habit.
Zero-copy transfer
When a consumer fetches data that is already in the page cache, Kafka can ask the operating system to send it straight from the cache to the network card without copying it through the application’s memory. This “zero-copy” approach saves processor time and memory bandwidth. Note that it works best when data is not encrypted by the broker itself. When TLS is enabled, data must be encrypted in the broker process, so this shortcut is lost for those connections.
Flushing to disk
Kafka writes data into the page cache and lets the operating system flush it to disk in the background. It relies on replication, not immediate disk flushes, for durability, since other brokers hold copies. Forcing a flush after every message is possible through settings, but it sharply reduces throughput and raises latency, and is rarely needed.
Append-only log
Simple sequential writes, no random updates, and easy cleanup by deleting old segments.
Batches everywhere
Batches travel from producer to disk to consumer without being split apart.
Memory as cache
The operating system keeps recent data in memory for fast reads by tailing consumers.
Replication for durability
Safety comes from copies on other machines, not from slow forced flushes.
Broker threads and queues
A broker has network threads that read requests from connections, a queue of waiting requests, and request handler threads that do the actual work. When traffic is light, a request passes through immediately. When threads are busy, requests wait in the queue, and this waiting is a major source of latency spikes. Monitoring the idle percentage of these thread pools tells you how close the broker is to the point where latency starts to climb.
Latency rises slowly at moderate load and then sharply near capacity, just as a highway flows smoothly until it is nearly full and then jams. Plan to run brokers well below their limits if latency matters.
9Partitions: Parallelism with a Price
Partitions are how Kafka spreads work across machines. More partitions can lift throughput, but they bring costs that show up in latency and stability.
How partitions raise throughput
A topic is split into partitions, each an independent log. Different partitions can live on different brokers and be read by different consumers in a group at the same time. More partitions therefore mean more parallel writing and reading. A single partition is limited by what one leader and one consumer can handle, so a very busy topic needs many.
A supermarket with one checkout lane has a hard limit on customers per hour, no matter how fast the cashier is. Opening more lanes raises the limit. But each lane needs a cashier, a till and floor space, and if there are far more lanes than customers, most sit idle while managers spend time supervising them.
flowchart TB
T["Topic with 6 partitions"] --> P0["Partition 0 on Broker 1"]
T --> P1["Partition 1 on Broker 2"]
T --> P2["Partition 2 on Broker 3"]
T --> P3["Partition 3 on Broker 1"]
T --> P4["Partition 4 on Broker 2"]
T --> P5["Partition 5 on Broker 3"]
P0 --> C1["Consumer A"]
P3 --> C1
P1 --> C2["Consumer B"]
P4 --> C2
P2 --> C3["Consumer C"]
P5 --> C3
The costs of too many partitions
Smaller batches
The same message rate spread across more partitions gives each a thinner trickle, so batches fill more slowly and efficiency falls.
More buffers
Producers and consumers hold buffers per partition, so memory use grows with partition count.
More work for followers
Each partition has followers fetching from the leader, and many small fetches are less efficient than a few large ones.
Longer failovers
When a broker fails, leaders for all its partitions must move. More partitions mean a longer period of unavailability for some of them.
More open files
Each partition has several log files, and brokers must keep many of them open.
How many partitions?
A widely used method is to estimate the throughput you want for the topic, measure what one partition can handle for producing and for consuming, and divide. Add headroom for growth, since increasing partitions later changes which partition a key maps to and can disturb ordering for keyed data. Avoid absurd numbers chosen “just in case”. Many production topics work well with a dozen to a few dozen partitions, with only the busiest having more.
Keys and ordering
Messages with the same key go to the same partition, which preserves their order. If one key is far busier than the others, its partition becomes a “hot” partition, limiting throughput and raising latency for that key regardless of how many partitions exist. Choosing keys that spread evenly is as important as choosing the partition count.
One overloaded partition can make a whole consumer group look slow. Always look at per-partition lag and rates, not only totals, when hunting for latency problems.
Adding partitions later
Increasing the partition count of an existing topic is possible, but it changes the mapping from keys to partitions. Messages for a key that used to land in one partition may now land in another, so consumers that rely on all events for a customer arriving in one place can see order disturbed during the change. Teams that use keyed data therefore plan a comfortable partition count at the start, or create a new topic and migrate carefully. This is one more reason to choose the number from measurement and growth forecasts instead of guessing.
Reducing the partition count is not supported directly at all. A topic with too many partitions usually needs to be replaced by a new one, which is a heavier operation. The lesson is to start moderate and grow, rather than start huge and be unable to shrink.
10The Consumer Side of the Balance
Consumers make their own trade-off between reading in big, efficient gulps and reacting to each message as soon as it appears.
Fetch settings
A consumer reads by sending fetch requests. Two settings shape how the broker answers.
| Setting | What it does | Throughput view | Latency view |
|---|---|---|---|
| fetch.min.bytes | The broker holds the reply until at least this much data is available | Larger values mean fewer, bigger responses | Larger values may delay delivery |
| fetch.max.wait.ms | The longest the broker will wait for the minimum amount | Longer waits allow fuller responses | Shorter waits cap the delay |
| fetch.max.bytes and max.partition.fetch.bytes | Upper limits on the size of a reply | Larger limits move more per request | Larger replies take longer to process |
| max.poll.records | Records returned to the application by each poll | Larger values reduce loop overhead | Larger values lengthen the time between polls |
A newspaper delivery person can ring your doorbell whenever a single article is printed, or bring the whole paper once each morning. Ringing constantly gets you news instantly but wears everyone out. A daily paper is efficient but stale by evening. Fetch settings let you pick the delivery rhythm.
The default is latency-friendly
By default, the minimum fetch size is one byte, so the broker replies as soon as any data exists. This favours low latency, at the cost of many small responses when traffic is light. Raising the minimum lets the broker collect more before replying, reducing request counts on busy clusters, but every message may wait up to the maximum wait time if traffic is slow.
Processing time is part of the story
The time a consumer takes to process each batch often exceeds all Kafka delays combined. If processing a batch takes longer than the allowed gap between polls, the consumer is considered dead and removed from its group, triggering a rebalance that pauses reading for everyone. Big poll sizes with slow processing are a classic cause of this trouble, which again turns “throughput per poll” into a latency and stability problem.
Parallel consumption and lag
Adding consumers to a group, up to the number of partitions, increases throughput. Consumer lag, the number of messages waiting to be read, is the key sign of whether consumers keep up. Stable low lag means throughput is adequate. Growing lag means the consumers are slower than the producers, and end-to-end latency will grow along with it.
Express lag as time, “this group is twelve seconds behind”, rather than only as a message count. It links directly to latency goals and is easier for non-specialists to understand.
Rebalances and the price of stopping
Whenever consumers join or leave a group, partitions are reassigned in what is called a rebalance. During older-style rebalances, every consumer briefly stops reading, which adds a pause to end-to-end latency for the whole group. Frequent restarts, long processing gaps or unstable networks cause repeated pauses. Newer cooperative rebalancing and static membership reduce the disruption by moving only the partitions that must move and by letting a restarted consumer keep its old place. For latency-sensitive applications, stability of the group is as important as raw speed.
11The Latency Budget
When a business gives you a target such as “under 200 milliseconds”, you must split it among all the stages. That split is called a latency budget.
Spending the budget
Imagine a goal of 200 milliseconds from event creation to a consumer finishing its work. You cannot spend it all on Kafka. Some goes to the application creating the event, some to the network, some to Kafka, some to processing. Writing the budget down makes each team responsible for a slice and shows which slice is too big.
flowchart LR
A["Create event: 10 ms"] --> B["Producer wait: 10 ms"]
B --> C["Send and replicate: 20 ms"]
C --> D["Wait for consumer fetch: 10 ms"]
D --> E["Consumer processing: 100 ms"]
E --> F["Spare margin: 50 ms"]
Planning a morning commute, you allow ten minutes to get ready, fifteen to reach the station, five waiting for the train and thirty on the train. If the total must be under an hour, you notice that a one-minute saving at home matters little when the train takes thirty. A budget shows where saving effort actually pays off.
Where Kafka teams find savings
Cut deliberate waiting
Reduce linger time, minimum fetch bytes and maximum fetch wait if their sum is a large share of the budget.
Keep data close
Place producers, brokers and consumers in the same region and, where possible, the same zone. Every extra network hop adds time.
Avoid slow brokers
Find and fix brokers with slow disks or heavy load, since they hold back acknowledgements for many partitions.
Speed up processing
Often the largest slice. Reduce per-message work, cache lookups and avoid slow remote calls in the consumer loop.
Stay below saturation
Keep brokers and consumers comfortably under their limits so queues stay short.
Adding all the averages together understates the worst case. Budget with percentiles, because the worst stage for one message is rarely the worst stage for another, and the total tail can be larger than expected.
12Three Tuning Profiles
Putting the dials together gives three typical starting points. Treat them as starting points to test, never as final answers.
| Dial | Throughput-first | Balanced | Latency-first |
|---|---|---|---|
| Linger time | Tens of milliseconds | A few milliseconds | Zero or very small |
| Batch size | Large, such as hundreds of kilobytes | Moderate, the default or somewhat larger | Default or smaller |
| Compression | LZ4 or Zstandard | LZ4 | None or LZ4 |
| Acknowledgements | All, with idempotence, for safety | All, with idempotence | All where data matters; a lower level only for data that can be lost |
| Consumer minimum fetch | Larger, tens of kilobytes or more | Small | One byte |
| Consumer maximum wait | Hundreds of milliseconds | Around the default | Short |
| Partitions | Enough for parallel load | Moderate | Modest, to keep batches and queues efficient |
| Broker headroom | Run hotter | Moderate | Run cool, well below saturation |
Log and metrics pipelines
Huge volume, tolerant of seconds of delay. Use big batches, compression and larger fetches to move the most data per dollar.
Business events
Orders, payments and inventory changes. Keep safety high with acknowledgement of all and accept small batching delays of a few milliseconds.
Real-time signals
Fraud scoring, alerts and live personalization. Keep waiting near zero, run brokers cool and watch percentiles closely.
A shipping company has freight trains for bulk cargo, delivery vans for city parcels and motorbike couriers for urgent documents. Each is best for its purpose. A wise company does not force every parcel onto a single kind of vehicle, and a wise Kafka team does not force every topic onto a single configuration.
Different settings for different topics and clients
Producer and consumer settings belong to each client application, not to the whole cluster. This means you can run a latency-focused payment service and a throughput-focused log shipper on the same cluster, each with its own client settings. Use quotas on the brokers to stop the bulk applications from crowding out the latency-sensitive ones, and consider separate clusters when the workloads are extreme and clash.
Document the profile chosen for each application and the reason. When someone later asks why linger time is twenty milliseconds, the answer should be written down, together with the measurements that supported it.
Revisiting the profile over time
Workloads change. A service that launched with a few hundred messages a second may grow to tens of thousands, and a profile that was ideal at launch can become wasteful. Review each application’s settings whenever traffic doubles, message sizes change, hardware is replaced or the business goal shifts. A quarterly look at the dashboards, with a short note on whether the balance still matches the goal, prevents slow drift into a configuration nobody chose on purpose.
13Ordering, Idempotence and Transactions
Correctness features are not free. Each one asks the system to do a little more work, and that work has a shape you should understand.
Ordering and retries
Kafka guarantees order within a partition. Suppose a producer sends batch one and batch two without waiting, and batch one fails and is retried. Without care, batch two might be stored first, and the order would be wrong. In the past, the safe answer was to allow only one outstanding request, which hurt throughput badly. With idempotent producers, the broker tracks sequence numbers and can keep order even with up to five outstanding requests, so you keep both order and speed.
If you mail several numbered chapters of a book to a publisher, the publisher can rebuild the right order from the numbers even if the envelopes arrive scrambled. Numbering costs almost nothing, and it means you can post all chapters at once instead of waiting for a receipt after each.
The cost of idempotence
Idempotence adds a small header to each batch and a small amount of bookkeeping on the broker. In practice the impact on throughput and latency is very small, and it is enabled by default in recent client versions. Its main requirement is acknowledgement of all, which is where any noticeable latency effect comes from.
The cost of transactions
Transactions let a producer write to several partitions atomically, so that consumers see all or none of the writes. They add coordination: the producer registers partitions with a transaction coordinator, and commit or abort markers are written into each partition. This adds latency, particularly if transactions are very short, because the fixed cost of coordinating is paid every time.
| Feature | Extra cost | How to reduce the pain |
|---|---|---|
| Idempotent producer | Tiny bookkeeping, needs acknowledgement of all | Usually nothing needed; keep it on |
| Transactions | Coordination calls and commit markers per transaction | Group more records into each transaction, and choose the commit interval deliberately |
| Read-committed consumers | Must wait until a transaction is complete before seeing its records | Keep transactions short so open ones do not block readers |
The commit interval trade-off
Stream processing applications that use exactly-once processing commit a transaction at regular intervals. A short interval gives fresh results to read-committed consumers but pays the coordination cost more often. A long interval reduces overhead and raises throughput but delays visibility. Kafka Streams, for example, uses a much shorter default commit interval when exactly-once is turned on than when it is off, showing that the designers considered this trade-off carefully.
A long-running open transaction stops read-committed consumers from moving past it in that partition. One slow producer can therefore stall the readers of many partitions. Keep transactions short and set sensible timeouts.
14Hardware, Network and Placement
Settings can only do so much. Eventually the machines, disks and network draw the real boundaries.
Disks
Because Kafka writes sequentially, ordinary disks can deliver good throughput, and many clusters use several disks per broker. Solid-state drives help most with latency variation, since they handle simultaneous reads and writes and recover quickly from bursts. Slow or failing disks are a common hidden cause of latency spikes, because a single slow broker can hold back the acknowledgements of every partition it leads.
Memory
Memory is important mostly as page cache. A common approach is a moderate Java heap and the rest of the memory left to the operating system. If the working set of recent data fits in the cache, consumers reading near the end of the log get answers without touching the disk, which keeps latency low.
A librarian keeps the books people ask for most on the desk instead of walking to the stacks each time. The desk is the page cache. When many readers ask for old books from deep storage, the librarian must walk to the stacks, and everyone waits longer. Consumers reading old data cause the same effect, evicting recent data from the desk.
Network
Network bandwidth often becomes the first limit. Each message is received once, replicated to followers, and sent to each consumer group, so traffic multiplies. A topic with three copies and three separate consuming applications generates far more network traffic than its incoming rate suggests. Compression, sensible replication factors and avoiding needless consumers all help.
Distance and placement
Light in fibre still takes time. A round trip inside one data center takes a fraction of a millisecond, between availability zones a millisecond or a few, and across continents tens to hundreds of milliseconds. Acknowledgement of all across distant regions therefore adds noticeable latency. Many organizations run a Kafka cluster in each region and copy data between them with tools such as MirrorMaker or Confluent’s Cluster Linking, accepting a delay in the copy in exchange for fast local writes.
Cloud considerations
In the cloud, data moving between availability zones is often charged. Spreading replicas across zones improves resilience but adds cost and a little latency. Consumers can be allowed to read from a nearby replica instead of the leader in newer Kafka versions, which saves cross-zone traffic and can lower latency. Instance types with fast local or well-provisioned network disks matter, and burstable disks can suddenly slow down when their credits run out.
Before tuning software, check that no machine is silently slower than the rest. One broker with a degraded disk or noisy neighbour can dominate the 99th percentile for the entire cluster.
15Benchmarking Without Fooling Yourself
A tuning change is only an improvement if a fair test says so. Careless benchmarks produce confident nonsense.
Start from a goal and a baseline
Write down the target throughput and latency percentiles, measure the current system under a realistic load, and record the numbers. Without a baseline, you cannot say whether a change helped. Change one setting at a time, run the same test, and compare.
Use realistic data and load
Message size, key distribution, compression ratio and traffic pattern all affect results. Random test data compresses very differently from real data. A steady synthetic load misses the bursts of a real day. Where possible, replay a sample of production-like data at production-like rates.
Testing a new road surface with a single car on an empty road tells you nothing about rush hour. A fair test uses the mix of vehicles and the crowding that real drivers produce.
Tools that come with Kafka
Kafka ships with performance test tools for producers and consumers. They send a chosen number of records of a chosen size at a chosen rate, and report throughput and latency percentiles. They are useful for quick comparisons of settings, though they do not reproduce your application’s real behaviour. Other community tools generate workloads and collect end-to-end latency, and the open-source OpenMessaging Benchmark has been used to compare messaging systems under common scenarios.
Common benchmarking traps
Testing at maximum rate only
Pushing as much as possible measures raw capacity, but says little about latency at your normal load. Test at several fractions of capacity to see how latency grows.
Watching only averages
Collect percentiles and maximums, or slow spikes vanish from the report.
Ignoring warm-up
The first minutes include cold caches and just-in-time compilation. Discard them or run long enough for them not to matter.
Load generator limits
If the test machine itself is saturated, you are measuring the test machine, not Kafka. Watch the generator’s processor and network as well.
Missing the stalls
If a test tool waits for slow replies before sending the next request, it accidentally hides the extra delay that would have hit requests during the stall. This is known among performance engineers as coordinated omission, and it makes results look better than reality.
Different conditions each run
Other workloads, background maintenance, or different data sizes change results. Keep everything else fixed.
Plot latency against load. A healthy curve stays flat for a long stretch and then bends upward. The place where it bends is your practical capacity, and you should operate comfortably to the left of it.
16Monitoring the Balance
Kafka publishes metrics that show exactly where you sit on the throughput and latency scale. Watching them lets you tune with evidence.
| Where | Metric | What it reveals |
|---|---|---|
| Producer | batch-size-avg and records-per-request-avg | Whether batching is working |
| Producer | request-latency-avg and max | How long the broker takes to acknowledge |
| Producer | record-queue-time-avg | How long records wait in the buffer, the direct cost of linger |
| Producer | compression-rate-avg | Whether compression pays off |
| Producer | buffer-available-bytes and bufferpool-wait-ratio | Whether back-pressure is building |
| Broker | Request time percentiles by stage | Where inside the broker time is spent |
| Broker | Request handler and network processor idle percentage | How close the broker is to saturation |
| Broker | Bytes in and out per second | Actual throughput |
| Broker | Under-replicated partitions | Whether followers keep up |
| Consumer | records-lag-max and lag in time | Whether consumers keep up |
| Consumer | fetch-latency-avg and fetch-size-avg | Whether fetches are efficient |
| Consumer | time-between-poll-max | Risk of being removed from the group |
flowchart TB
S["Latency is too high"] --> Q1{"Is producer queue time high?"}
Q1 -- "yes" --> A1["Reduce linger time or batch size"]
Q1 -- "no" --> Q2{"Is broker request time high?"}
Q2 -- "yes" --> A2["Check thread idle percentages, disks, replication"]
Q2 -- "no" --> Q3{"Is consumer lag growing?"}
Q3 -- "yes" --> A3["Speed up processing or add consumers"]
Q3 -- "no" --> A4["Check network path and clocks"]
When a package arrives late, you check the tracking history: was it slow to leave the warehouse, delayed on the road, or left sitting at the local depot? Kafka’s metrics play the same role, showing at which stage the delay was added.
Logging and correlation
Metrics show that latency rose. Logs show why. Broker logs may reveal garbage collection pauses, disk errors or leader changes at the exact time. Keep machine clocks synchronized so that timestamps line up, and consider putting a creation timestamp inside each message so consumers can compute true end-to-end age.
Average latency almost always looks healthier than what users feel. Alert on percentiles, and make dashboards show the 99th percentile prominently.
Building a useful dashboard
A single page that shows the essentials helps everyone speak the same language. Put message rate and byte rate at the top to show throughput. Beside them, show the 50th, 95th and 99th percentile of produce request time, and consumer lag expressed in seconds. Below, show thread idle percentages, under-replicated partitions and disk usage to explain causes. Mark deployments and configuration changes on the graphs, so a sudden shift can be linked to what changed. When throughput and latency graphs move together, you can see the trade-off with your own eyes, and you can tell whether a tuning change moved the balance in the direction you wanted.
17Security and Its Performance Cost
Encryption and access control are essential, and they have a measurable price in throughput and latency. Knowing the price lets you budget for it rather than switch protection off.
Encryption in transit
TLS encrypts traffic between clients and brokers and among brokers. It adds a one-time handshake cost when a connection opens and a continuous processor cost to encrypt and decrypt data. On modern processors with hardware support the cost is moderate, but it can reduce peak throughput noticeably and increase broker processor use. As described earlier, TLS also prevents the zero-copy shortcut for reads, since the broker must handle the data to encrypt it.
Sending valuables in a locked armoured van is safer than sending them in an open truck, and it takes a bit longer to load, lock and unlock. Nobody sensible drops the lock to save a minute. They instead plan the schedule so the extra minute is included.
Authentication and authorization
Authentication proves who the client is, using methods such as mutual TLS, SASL with passwords or tokens, or Kerberos. It costs time at connection setup, so applications that open many short-lived connections pay repeatedly. Long-lived connections and shared producer instances avoid this. Authorization checks, through access control lists, happen on requests and are cached in memory, so their per-request cost is small unless the number of rules is enormous.
Quotas: fairness as a performance tool
Kafka quotas limit how much data a client or user can produce or consume, and how much broker processing time they can use. Quotas protect latency-sensitive applications from noisy neighbours, and they are a security and stability feature too. A client that exceeds its quota is slowed by the broker, which appears in metrics as throttle time. If you see throttle time, the “slowness” is a deliberate limit, not a fault.
Handshakes
Opening a secure connection takes several round trips. Reuse connections to pay once.
Encryption work
Processor time per byte. Size brokers with this in mind and use modern hardware support.
Lost shortcuts
Zero-copy reads are not available for encrypted connections, so plan more processor headroom.
Quotas
Protect important applications from being crowded out by bulk jobs.
Benchmark with security turned on. A test done without TLS may overstate what the production cluster can do, leading to under-sized brokers and surprising latency after launch.
18Patterns and Anti-Patterns
Recurring designs help teams manage the trade-off well, and recurring mistakes damage it.
Patterns worth copying
Separate topics or clusters by service level
Keep urgent, low-latency events apart from bulk data. Give each its own topics, quotas and, when the difference is large, its own cluster.
Why it works
Bulk traffic cannot crowd the request queues or the disks that urgent traffic depends on, and each side can be tuned for its own goal.
Everyday picture
Airports have separate lanes for express travellers and for large tour groups, so one does not slow the other.
Small bounded linger with compression
Allow a few milliseconds of linger and enable a fast codec, then measure. This often improves throughput dramatically at a cost that users cannot notice.
Why it works
Latency is bounded by a known timer, while efficiency gains come from bigger batches and smaller data.
Everyday picture
A shuttle that waits at most two minutes for more passengers is still convenient, and far more economical than one that leaves with each rider.
Scale consumers with partitions, and keep processing light
Match the number of consumers to partitions, and move slow work, such as calls to remote services, out of the polling loop or into bounded worker pools.
Why it works
Consumers keep polling regularly, avoiding group removals, and lag stays low.
Everyday picture
A restaurant host seats guests quickly and hands orders to the kitchen instead of cooking each meal personally.
Anti-patterns to avoid
What people do
They copy a set of “fast” settings from a blog post or another team without measuring their own workload.
Why it hurts
Different message sizes, rates and hardware behave differently. Settings tuned for a bulk pipeline may cripple a real-time service.
What to do instead
Use published advice as a hypothesis, test it against your own traffic, and keep the measurements.
What people do
They set acknowledgements to zero or one everywhere to make graphs look better.
Why it hurts
Data loss during broker failures becomes silent. The speed gain is real but small next to the risk.
What to do instead
Keep acknowledgement of all with sensible replication, and reduce latency by other means, such as less waiting and better placement.
What people do
They create thousands of partitions per topic in the hope of unlimited scale.
Why it hurts
Batches become tiny, memory use grows, failovers take longer and the cluster becomes harder to run.
What to do instead
Size partitions from measured per-partition throughput with reasonable headroom.
What people do
They create a new producer for every message, or send one message and flush immediately every time.
Why it hurts
Connections, buffers and handshakes are rebuilt constantly, and batching never occurs. Both throughput and latency suffer.
What to do instead
Create one long-lived producer per application, share it among threads, and let it batch.
What people do
They run brokers at ninety percent of capacity to save money and still expect fast responses.
Why it hurts
Queues form near saturation, so latency rises sharply and a small burst or a single broker failure can cause an outage.
What to do instead
Keep headroom for bursts and for the loss of a broker, and add capacity before the latency curve bends.
19Best Practices and Common Mistakes
A short list of habits that keep the throughput and latency balance healthy, and traps that catch almost every team once.
Do
- Write down throughput and latency goals, using percentiles, before tuning.
- Measure a baseline and change one setting at a time.
- Keep idempotence and acknowledgement of all for important data.
- Allow a small, bounded linger time and use a fast compression codec.
- Reuse one producer per application and let it batch.
- Choose keys that spread load evenly across partitions.
- Watch per-partition lag, thread idle percentages and percentile request times.
- Keep brokers well below saturation and leave memory for the page cache.
- Benchmark with security and realistic data turned on.
- Give different workloads different profiles, quotas or clusters.
Do not
- Tune by averages alone.
- Trade safety for speed without recording the decision.
- Copy settings from another system without testing them.
- Create partitions far beyond what the load needs.
- Flush after every send, or force disk flushes on every message.
- Assume the broker is the slow part before checking consumers and downstream systems.
- Put slow remote calls inside the consumer polling loop.
- Test only at maximum load, or only with an idle cluster.
- Forget that one slow broker can slow a whole cluster.
Mistakes beginners meet first
“Zero linger means zero delay”
Even with no linger, messages can still wait behind requests already in flight, and network and replication time remain. Zero linger reduces waiting but does not remove latency.
“Bigger batch size is always better”
If messages arrive slowly, a huge batch never fills and only the timer matters, while memory is wasted. Match batch size to what actually accumulates within your linger time.
“More consumers than partitions will speed things up”
In a group, each partition is read by one consumer at a time, so extra consumers sit idle. Add partitions or speed up processing instead.
“Low producer latency means low end-to-end latency”
The message may be stored in milliseconds but read minutes later by a lagging consumer. Measure the whole path.
“Compression is always worth it”
Already compressed or encrypted payloads gain almost nothing, and the processor cost remains. Check the compression ratio.
“Throughput tests prove we are ready”
A test showing high throughput does not show good latency, especially at the tail. Test latency at your expected load, too.
“One hot key is fine”
A single very busy key floods one partition and one consumer, so adding partitions does not help. Redesign the key or split the load.
Tuning a car engine for a race track ruins its fuel economy in city traffic, and tuning it for economy makes it sluggish on the track. A good mechanic asks where you will be driving before touching anything. Good Kafka tuning starts with the same question.
A short tuning routine
State the goal
For example: 99th percentile end-to-end under 300 milliseconds at 40,000 messages per second.
Measure the baseline
Record current throughput, producer queue time, request times and consumer lag under a realistic load.
Find the biggest slice
Use the latency budget and metrics to see where the most time is spent.
Change one dial
Adjust linger, batch size, compression, fetch settings or partitions, one at a time.
Re-measure and decide
Keep the change only if the goal moves closer and no other goal suffers.
Write it down
Record the setting, the reason and the numbers, and add alerts for the metrics that protect the gain.
Culture matters as much as settings
Teams that handle this trade-off well share a habit: they treat performance goals as requirements with owners, not as afterthoughts. Someone owns the latency target, someone owns the throughput target, and both look at the same dashboards. When a new feature threatens either goal, the conversation happens early, with numbers, rather than after users complain. This shared discipline keeps tuning from becoming an argument between people who each optimize only their own metric.
20Real-World Stories
Public talks, papers and documentation from well-known organizations show the trade-off in action.
LinkedIn and the early benchmark
LinkedIn engineers, who created Kafka, published a well-known benchmark in 2014 showing about two million writes per second on three inexpensive machines. The article credited batching, sequential disk access and the operating system’s cache, the same ideas covered in this guide, and it showed why Kafka became known for throughput.
Confluent and the tuning guidance
Confluent, the company founded by Kafka’s creators, has published white papers and articles on optimizing Kafka for throughput, latency, durability and availability. They present the same dials described here, such as batching, compression, acknowledgements and partition counts, and stress that goals conflict and must be balanced deliberately.
Uber’s very large deployments
Uber has described in engineering posts how it runs Kafka at very large scale, with attention to consumer behaviour, retry handling and tiering different traffic. The common lesson is that at scale, teams isolate workloads and automate monitoring so that one heavy user cannot harm the latency of another.
Netflix and data pipelines
Netflix has presented on its use of Kafka in the pipelines that carry events and logs for analytics. Such pipelines favour throughput and resilience, so they tolerate small delays in exchange for enormous volumes, an example of the throughput-first profile.
Financial services and low-latency needs
Banks and payment companies often use Kafka to carry events between services. Public talks from such firms describe using acknowledgement of all and replication across zones for safety, then working hard on placement, thread capacity and consumer processing to keep latency within their goals. Truly microsecond trading paths typically use specialized systems instead, showing that Kafka is best for millisecond-scale and higher latency targets.
Cloudflare and analytics at scale
Cloudflare has written about running Kafka to handle very large volumes of analytics and logging data. Articles on its engineering blog discuss compression choices and the trade-off between space savings and processor use, reinforcing that compression is a lever worth testing.
Managed services
Managed Kafka offerings such as Amazon MSK, Confluent Cloud and Aiven remove much machine management, and they document recommended settings. The customer still chooses client settings, partition counts and keys, so the trade-offs in this guide remain the customer’s responsibility.
21Frequently Asked Questions
Short answers to questions that newcomers and experienced engineers ask often.
22Summary and Key Takeaways
If you keep only one page of this guide, keep this one.
Throughput and latency are two different measures of speed, and Kafka lets you slide between them. Grouping messages into batches, compressing them and fetching them in larger pieces moves more data for less effort, at the cost of making individual messages wait. Sending and reading eagerly does the opposite. Neither end is right for every job, so the skill lies in choosing where each application should stand, measuring the result and keeping enough headroom for bursts and failures.
Key Takeaways
- Throughput is how much, latency is how long. Measure them separately, and use percentiles for latency instead of averages.
- Batching is the heart of the trade-off. Bigger batches raise throughput and add waiting for the first message in each batch.
- Small dials, big effect. Linger time, batch size, compression, acknowledgements, fetch sizes and partition counts move the balance.
- Safety costs time, and it is usually worth it. Acknowledgement of all with sensible replication adds a few milliseconds and protects acknowledged data.
- Kafka is fast by design. Sequential writes, the page cache and zero-copy transfer explain its throughput, and TLS and slow disks can dilute them.
- Partitions give parallelism with a price. Too many shrink batches and lengthen failovers, and hot keys defeat them.
- Think end to end. Producer acknowledgement latency is not the same as the time until a consumer finishes its work, and processing often dominates.
- Use a latency budget. Split the target across stages and spend effort where the biggest slice is.
- Measure honestly. Benchmark with realistic data, security turned on, several load levels and tail percentiles.
- Choose per workload. Give bulk and urgent traffic different profiles, quotas or clusters, and write down each decision.