What Is Batch Size Tuning in Kafka?
How Kafka producers group messages into batches, how batch size, linger time, compression and memory work together, and how to tune them for the right balance of throughput, latency and cost without guessing.
Imagine a delivery van that leaves the warehouse the moment a single parcel is ready. The van drives across the city, drops one parcel, and returns. Now imagine a van that waits ten minutes, loads fifty parcels, and makes one trip. The second van delivers far more parcels per hour with the same fuel and the same driver, but the first parcel in the van waits ten minutes before it starts its journey. Somewhere between “leave now” and “wait until the van is full” lies the sweet spot for your business. Finding it is the whole art of batch size tuning.
Apache Kafka producers face exactly this choice thousands of times per second. Every message can be sent alone, or grouped with others into a batch. Sending alone is fast for that one message but wasteful for the system. Sending in batches is efficient but adds a small wait. In this guide you will learn what a batch is, which settings control it, how to measure whether your batches are healthy, how to tune them step by step for different workloads, and which mistakes cause slow producers, wasted memory and unhappy brokers.
1What Batch Size Tuning Means
Batch size tuning is the practice of choosing how many bytes and how much waiting time a Kafka producer uses to group messages before sending them.
Start with the basic words. A message, also called a record, is a small piece of data such as “customer 17 clicked the buy button”. A producer is a program that sends messages to Kafka. A topic is a named log where messages are stored, and each topic is split into partitions, which are separate ordered logs that can live on different servers called brokers.
A batch is a group of messages that a producer sends to one partition in a single unit. Instead of sending one hundred separate messages over the network, the producer packs them into one batch, adds one header, and sends them together. The broker stores the batch as it arrived and later hands it to consumers as a batch as well.
Batch size tuning means adjusting the settings that control batches so that the producer sends the right amount of data at the right times. The main settings are batch.size, the maximum size of a batch in bytes, and linger.ms, how long the producer may wait for more messages before sending. Around them sit compression, memory, request limits and acknowledgment settings, which all interact.
Think of a lift in an office tower. If the lift leaves the moment one person steps in, it makes many trips carrying one person each. The queue grows and the lift wears out. If the lift waits until it is completely full, the first person may stand there for minutes. A sensible building programs the lift to leave when it is full or after a short wait, whichever comes first. That is exactly how batch.size and linger.ms cooperate.
The goal of tuning
Higher throughput
Send more messages per second using fewer network requests and less CPU on both producers and brokers.
Acceptable latency
Keep the delay between creating a message and storing it within what the business can accept.
Efficient resources
Use memory, network and disk sensibly, without waste or sudden spikes.
Tuning is not about finding the biggest number. It is about choosing the smallest batch settings that give your throughput target, so that latency and memory stay low.
2Why Batching Matters
Every request has a fixed cost that does not depend on how much data it carries. Batching spreads that cost over many messages.
When a producer sends data to a broker, a lot happens besides moving the message bytes. The producer builds a request with headers. The operating system copies data into network buffers. The network carries packets and confirmations. On the broker, a network thread reads the request, a request handler thread checks it, writes it to the log, and waits for replicas to copy it before acknowledging. All of this work has a fixed part and a part that grows with the number of bytes. Sending ten thousand tiny requests pays the fixed part ten thousand times. Sending one hundred batches pays it a hundred times.
| Cost | Without batching | With batching |
|---|---|---|
| Network round trips | One per message | One per batch |
| Broker request handling | Repeated for every message | Shared by all messages in the batch |
| Compression efficiency | Almost none, since each message is tiny | Good, because similar messages sit together |
| Disk writes | Many small appends | Fewer, larger appends |
| Replication overhead | Many small fetches by followers | Fewer, larger fetches |
| CPU use on brokers | High | Much lower |
Posting a hundred letters one by one means a hundred trips to the post box, a hundred stamps handled and a hundred queue waits. Putting them in one bag and dropping the bag once costs the same walk. The letters are the messages, the bag is the batch, and the walk is the fixed cost of a request.
What goes wrong when batches are too small
The chatty producer
A service sends fifty thousand small events per second with the default settings, and each event travels in its own request because the producer is idle whenever an event arrives. Broker CPU climbs, the request queue grows, and latency rises for everyone sharing the cluster, even though the total number of bytes is modest.
The compression that does nothing
A team turns on compression to save network and disk, but sees almost no benefit. The reason is that compression works on the batch, and their batches contain only one or two messages. There is nothing to compress against.
Many people think batching is a special mode to turn on. In fact, Kafka producers always batch. What you tune is how large and how patient the batches are.
3The Anatomy of the Producer Pipeline
To tune batching you need a picture of what happens between your code calling send and the broker replying.
A Kafka producer is not a single loop. It is a small pipeline with two kinds of threads. Your application threads call send and return quickly. A separate background sender thread does the network work. Between them sits a shared memory area called the record accumulator, which holds batches waiting to be sent.
flowchart LR APP["Application Threads calling send"] --> SER["Serializer"] SER --> PART["Partitioner chooses partition"] PART --> ACC["Record Accumulator with one batch queue per partition"] ACC --> SEND["Sender Thread"] SEND --> NET["Network Client with requests per broker"] NET --> BR["Kafka Broker"] BR --> ACK["Acknowledgment"] ACK --> CB["Callback to application"] BUF["Buffer Memory Pool"] --> ACC
The stages in plain words
Your code calls send
The message goes to the producer library, which returns almost at once, without waiting for the broker.
Serialization
The key and value are turned into bytes, because Kafka stores only bytes.
Partitioning
The partitioner picks the partition. If the message has a key, a hash of the key decides. If not, the producer chooses a partition using its own strategy.
Accumulation
The message is appended to the open batch for that partition, or a new batch is started using memory from the buffer pool.
Sending
The sender thread collects ready batches, groups them by destination broker, and sends one request per broker.
Acknowledgment
The broker replies once the data is stored as required. The producer completes the message and runs your callback.
Batches are built per partition, but requests are built per broker. One request may carry several batches, one for each partition led by that broker. Tuning therefore affects both the batch and the request.
Think of a mail room. Workers drop letters into pigeonholes, one per destination city. A driver comes by regularly and collects whatever is in the holes for each road he travels, tying it up in one bundle per road. The pigeonholes are batches per partition, and the bundle per road is the request per broker.
4What a Batch Really Is
A batch is not just a convenient bundle in memory. It is the same structure that goes over the network and onto the disk.
Kafka uses a single format for a batch of records, called a record batch, in memory, on the wire and in the log files on the broker. This design means the broker can usually write what it receives directly to disk and send it later to consumers without unpacking and repacking it. That is one of the reasons Kafka is so fast.
Batch header
Holds information about the whole batch: the first offset, the number of records, timestamps, the producer identity and sequence numbers used for idempotence, and the compression type. It is a fixed cost of roughly sixty bytes.
The records
Each record stores its key, value and headers, with small offsets relative to the start of the batch, which keeps per-record overhead low.
Optional compression
If compression is on, the records section of the batch is compressed as one block, so the block gets smaller as similar records sit together.
Why this matters for tuning
| Property of batches | Effect |
|---|---|
| Header cost is fixed per batch | Small batches pay this cost on few records, so it is proportionally larger |
| Compression works on the whole batch | Bigger batches usually compress better |
| Brokers store batches as received | Producer batch size influences disk layout and read efficiency |
| Idempotence numbers batches | Retries resend whole batches and brokers detect duplicates per batch |
| Consumers fetch batches | A consumer reads at least a whole batch, even if it wants only one record |
A batch is like a sealed shipping container with a single label on the door. The label costs the same whether the container holds one box or a thousand. Once packed, the container travels by ship, sits in a warehouse and is delivered without being opened. That is why packing it well at the start pays off all along the journey.
Do not confuse a batch with a request. A batch belongs to one partition. A request is a network message to one broker and may contain batches for many partitions. Settings such as batch.size apply to batches, while max.request.size applies to requests.
5The batch.size Setting
batch.size is the maximum number of bytes the producer will put into one batch for one partition.
The setting batch.size is measured in bytes. Its long-standing default is 16,384 bytes, which is 16 kibibytes. When the open batch for a partition reaches this size, the batch is closed and becomes ready to send. A new batch is opened for the next message. The word to notice is maximum. The producer never waits until a batch is full merely because you set a large number. It only uses batch.size as a ceiling.
What batch.size does and does not do
What it does
- Sets the largest size of one batch
- Decides how much memory is reserved for each open batch
- Triggers sending when a batch fills
- Limits how much data one partition adds to a request
What it does not do
- It does not make the producer wait for a full batch
- It does not limit the size of a single record
- It does not apply to the whole request
- It does not guarantee that batches reach this size
The memory side effect
The producer takes memory from its buffer pool in chunks of batch.size for each new batch. If you set batch.size to one megabyte and write to two hundred partitions, the producer may try to hold two hundred megabytes of open batches, far beyond the default buffer of thirty-two megabytes. When the pool is short of memory, sending slows and calls may block. So a very large batch.size is not free, and it must be balanced against the number of active partitions.
Records larger than batch.size
If a single record is bigger than batch.size, the producer does not refuse it. It creates a batch just big enough for that one record. This means large messages travel alone, and the batching benefit is lost for them. If most of your messages are larger than batch.size, raising batch.size, or reducing message size, is the first thing to consider.
Think of shipping crates with a maximum capacity. You can decide that each crate holds at most forty boxes. You are not forced to fill it before it leaves. But the warehouse must reserve floor space for one crate of that size for every destination, whether it is full or not. Choosing a huge crate size for many destinations quickly fills the warehouse floor.
For throughput-oriented producers, teams often raise batch.size to values between 64 and 256 kibibytes, but the right number depends on message size, partition count and memory. Treat any range as a starting point for measurement, not a rule.
6The linger.ms Setting
linger.ms is the small delay that gives a batch a chance to fill before it is sent.
The setting linger.ms tells the producer how many milliseconds it may wait for more messages to join a batch before sending it. Older Kafka versions default to zero, which means “send as soon as the sender thread is free”. Newer releases, starting with the Kafka 4.0 line, changed the default to a few milliseconds. Check the documentation of your version.
With a linger of zero, batching still happens, but only by accident. While the sender thread is busy with an earlier request, new messages pile up in the accumulator and are sent together in the next round. Under light traffic, however, each message may go alone, because the sender is idle and finds one message waiting. A small positive linger creates a deliberate window in which messages can gather.
| linger.ms value | Behavior | Best for |
|---|---|---|
| 0 | Send as soon as possible; batches form only under load | Extremely latency sensitive paths with low traffic |
| 1 to 5 | Tiny wait; often enough to collect a handful of messages | Interactive systems that want some batching at almost no delay |
| 10 to 50 | Meaningful wait; batches fill well at moderate rates | Most streaming pipelines and event collection |
| 100 or more | Long wait; large batches and best compression | Bulk transfer where seconds of delay are acceptable |
Linger is a maximum delay, not a fixed delay
Linger.ms does not add a fixed pause to every message. If the batch fills up sooner, it is sent immediately. Linger is the longest a message will wait. So at high traffic, where batches fill quickly, a large linger costs nothing, and at low traffic, it caps the extra delay at the linger value.
A shuttle bus at an airport leaves when it is full or after ten minutes, whichever comes first. At rush hour it leaves every couple of minutes because it fills quickly. In the quiet of night, passengers wait at most ten minutes. The ten-minute rule is linger.ms, and the seats in the bus are batch.size.
Adding a linger of twenty milliseconds adds up to twenty milliseconds to each message on this hop. If your service has a total latency budget of fifty milliseconds and calls the producer synchronously, a large linger may consume most of it. Decide the budget first.
7When Exactly Is a Batch Sent?
Several conditions can make a batch ready. Knowing them explains most of the surprises you will see in metrics.
flowchart TD
A["Message added to partition batch"] --> B{"Is batch full at batch.size"}
B -->|"Yes"| R["Batch is ready to send"]
B -->|"No"| C{"Has linger.ms passed since batch was created"}
C -->|"Yes"| R
C -->|"No"| D{"Is buffer memory exhausted or flush called"}
D -->|"Yes"| R
D -->|"No"| W["Keep waiting for more messages"]
R --> S["Sender thread includes batch in request to its broker"]
| Trigger | Explanation | What you will notice |
|---|---|---|
| Batch is full | The batch reached batch.size | High traffic: batches near their maximum size |
| Linger expired | The batch waited the allowed time | Moderate traffic: batches smaller than the maximum but consistent |
| Memory pressure | The buffer pool is full and other calls wait for space | Batches sent early and small; blocking in send calls |
| Flush or close | The application forces everything out | Bursts of small batches at flush time |
| Riding along | The sender is already sending to this broker and includes other waiting batches | Batches leaving before their own timers expire |
The ride-along effect
When at least one batch for a broker is ready, the sender builds a request for that broker and usually includes waiting batches for other partitions on the same broker, even if they are neither full nor expired. This means that the batch size you observe can be smaller than what linger.ms and batch.size alone would predict. It also means that spreading writes across many partitions on the same broker does not always cause the smallest possible batches, but it does cause smaller ones than concentrating writes would.
If your average batch size is far below batch.size and your linger is generous, look for the other triggers: memory pressure, frequent flush calls, or a very high number of partitions at low traffic per partition.
8Partitions, Keys and Batching
Batches are per partition, so how messages spread over partitions decides how easily batches fill.
Suppose a producer sends ten thousand messages per second. If all go to one partition, each batch fills quickly. If they are spread evenly over one hundred partitions, each partition receives only one hundred messages per second, and each batch takes one hundred times longer to fill. The same total traffic yields far smaller batches.
| Factor | Effect on batches |
|---|---|
| More partitions on the topic | Fewer messages per partition per second, so smaller batches at the same linger |
| Keyed messages with many distinct keys | Spread across all partitions, so each partition batch fills slowly |
| Keyed messages with a few hot keys | Some partitions receive nearly all traffic and fill fast; others stay almost empty |
| Messages without keys | The producer may choose partitions to help batching, as described below |
| Many producers instances | Each instance has its own batches, so total batch count multiplies |
Sticky partitioning for messages without keys
For messages without a key, older producers rotated through partitions message by message, which produced many tiny batches. Since Kafka 2.4, the default strategy is sticky: the producer sends all keyless messages to one partition until its batch is full or the linger time has passed, then switches to another partition. Over time the load is balanced, but each individual batch is larger. Later versions improved this by adapting to broker speed so that slow brokers receive fewer batches.
Imagine handing out flyers at a station with ten exits. Giving one flyer to each exit in turn means you are always walking. Standing at one exit until your stack for that exit is complete, then moving to the next, saves many steps and delivers the same total number of flyers over the day.
Production example: the partition count surprise
A team increases a topic from twelve to two hundred partitions to allow more consumers. Their producers, tuned earlier for twelve partitions, now build two hundred small batches at the same total rate. Request counts rise, brokers do more work per byte, and throughput drops until the team raises the linger time and reviews the batch settings for the new layout.
Adding partitions improves parallelism for consumers but weakens batching for producers. When you change partition counts, re-check your batch metrics.
9Compression and Batches
Compression and batch size are partners. Bigger batches compress better, and compressed batches move faster.
The producer setting compression.type chooses whether and how batches are compressed. The options are none, gzip, snappy, lz4 and zstd. Compression is applied to the whole batch, not message by message. Similar messages placed together share patterns, such as repeated field names in JSON, that the compressor can replace with short references.
| Type | Speed | Compression ratio | Typical use |
|---|---|---|---|
| none | Fastest | None | Already compressed or tiny data |
| lz4 | Very fast | Moderate | High throughput with low CPU cost |
| snappy | Very fast | Moderate | Similar to lz4; widely used in older setups |
| zstd | Fast to moderate | High | Best balance when network or disk cost matters |
| gzip | Slower | High | When compression ratio matters more than CPU |
How batch size affects compression
A batch containing one message of two hundred bytes compresses badly, because the compressor has nothing to compare it with. A batch of one hundred similar messages compresses very well. This is why two producers with identical data can see very different compression ratios: the one with better batching wins. When you tune batch size upward, expect the compression ratio to improve, up to a point of diminishing returns.
Packing suitcases for a family holiday, you fit more clothes if you fold and roll them together in one large bag than if you pack each shirt in its own small box. Compression finds the “folding” pattern across many messages. A batch with only one shirt has nothing to fold.
Where the CPU cost lands
The producer compresses batches, using its own CPU. Brokers normally store batches as they are, when the topic compression setting is left at its default of “producer”, so they do not spend CPU recompressing. Consumers decompress batches when reading. Spending CPU at the producer to save network, broker disk and consumer fetch bandwidth is often a good trade for high-volume topics.
The producer reports its average compression rate as a metric. If the ratio is close to one, either the data is not compressible or the batches are too small to compress well. Compare the ratio before and after changing batch size to see which one applies.
10Buffer Memory and Backpressure
Batches wait in memory. When memory runs out, the producer pushes back on your code.
The setting buffer.memory is the total memory the producer may use to hold batches that are waiting to be sent or waiting for acknowledgment. The default is thirty-two mebibytes. If your application produces faster than the brokers can accept, the buffer fills. When it is full, calls to send block for up to max.block.ms, which defaults to sixty seconds, and then fail with a timeout. This blocking is called backpressure, and it protects the producer from running out of memory.
flowchart TD
A["Application calls send"] --> B{"Is buffer memory available"}
B -->|"Yes"| C["Message joins a batch in the accumulator"]
B -->|"No"| D["Call blocks and waits"]
D --> E{"Space freed before max.block.ms"}
E -->|"Yes"| C
E -->|"No"| F["Send fails with a timeout error"]
C --> G["Sender thread sends the batch and frees memory after acknowledgment"]
G --> B
Why the buffer fills
Slow brokers
If brokers are overloaded, acknowledgments arrive late and batches stay in memory longer.
Network trouble
Lost connections or slow links delay requests and force retries.
Oversized batch settings
A large batch.size across many partitions reserves more memory than the pool contains.
Producing faster than the cluster can absorb
Sustained input above the capacity of the topic’s partitions and brokers always ends with a full buffer.
Picture the loading dock of a warehouse. Pallets waiting for trucks sit in a fixed-size yard. If trucks arrive slowly, the yard fills up, and the forklift drivers must stop bringing new pallets until space appears. If they wait too long, the manager gives up and cancels the order. The yard is buffer.memory, and the waiting is backpressure.
A bigger buffer only delays the moment it fills. If the cluster cannot keep up, more memory means longer delays and bigger loss if the process crashes. Find out why acknowledgments are slow before enlarging the buffer.
11Size Limits: Requests, Records and Brokers
Several limits sit around batches. If they disagree, messages are rejected.
| Setting | Where | Default | What it limits |
|---|---|---|---|
| batch.size | Producer | 16 KiB | Target maximum size of one batch for one partition |
| max.request.size | Producer | 1 MiB | Largest request, and the largest single record, the producer will send |
| message.max.bytes | Broker | About 1 MiB | Largest batch the broker will accept |
| max.message.bytes | Topic | Inherits the broker value | Per-topic override of the largest batch accepted |
| replica.fetch.max.bytes | Broker | 1 MiB | Amount followers fetch per partition when replicating |
How the limits relate
Notice that the broker limit and the topic limit apply to a batch, not to a single record, in modern Kafka versions. A producer batch that is larger than the broker limit is rejected with a “message too large” error. The producer limit max.request.size applies to the whole request, so a request carrying many batches cannot exceed it. To send large messages safely, all three levels must agree: the producer, the broker or topic, and the replication fetch size.
Decide the largest record you need
Measure the biggest realistic message, not the average.
Raise the topic limit first
Set the topic-level maximum so that the broker accepts the larger batch.
Make sure replication can keep up
Followers must be allowed to fetch batches of that size, or replication stalls.
Raise the producer request limit
Allow the producer to send requests that contain the larger batch.
Set consumer fetch limits
Consumers must be able to fetch a full batch from a partition, or they cannot read it.
For files, images or documents of many megabytes, store the payload in object storage and send only a reference through Kafka. This keeps batches small, brokers healthy and consumers quick.
12Acknowledgments, In-Flight Requests and Batching
How long a producer waits for the broker changes how many batches can be in motion, and so changes batching behavior.
The setting acks decides when the producer considers a batch delivered. With acks set to zero it does not wait at all. With one it waits for the leader to write. With all it waits until every in-sync replica has the batch. Stronger acknowledgment means the broker takes longer to reply, and the producer holds the batch in memory for longer.
The setting max.in.flight.requests.per.connection, by default five, limits how many unacknowledged requests the producer may have outstanding on one connection to a broker. While the connection is at its limit, new messages keep gathering in the accumulator and form larger batches. Slower acknowledgment therefore tends to produce larger batches by itself: the sender thread has less chance to send early, so more messages wait together.
| Setting | Effect on batching | Caution |
|---|---|---|
| acks all | Slower replies; batches wait longer and often grow larger | Best durability; requires healthy replicas |
| acks one | Faster replies; smaller natural batches | Data may be lost if the leader fails at the wrong time |
| acks zero | No waiting; batching depends on linger alone | No delivery confirmation at all |
| Higher in-flight limit | More parallel requests; less natural accumulation | With idempotence, keep at five or fewer to preserve ordering |
| Idempotence on | Batches are numbered so retries are deduplicated | Requires acks all and retries enabled |
Consider a courier who may have at most five unsigned parcels out at any time. If customers are slow to sign, the courier cannot leave with more parcels, so new orders pile up at the depot and are loaded together in the next big trip. A slow signature process leads to bigger loads, but also to longer waits.
Some teams lower acks to make producers faster instead of tuning batches. The lower setting risks data loss for a small, often unnecessary, gain. Tune batching first and keep durability settings at the level the business requires.
13The Throughput and Latency Trade-off
Every tuning choice moves you along one line: more waiting gives more efficiency, less waiting gives faster individual messages.
Throughput is how many messages, or bytes, the system moves per second. Latency is how long one message takes from creation to safe storage. Batching improves throughput because each request carries more data. It can worsen latency because messages wait for company. The two are in tension, but the tension is gentler than most people expect, because a small amount of waiting often brings most of the benefit.
flowchart LR A["Tiny batches and no linger"] --> B["Lowest wait per message"] A --> C["Highest request rate and CPU use"] D["Moderate batches and small linger"] --> E["Small added wait"] D --> F["Much lower request rate and good compression"] G["Huge batches and long linger"] --> H["Long wait for early messages"] G --> I["Little extra gain and more memory used"]
The shape of the curve
| Region | Typical settings | What you see |
|---|---|---|
| Too little batching | Linger 0, small batches, high partition count | Many small requests, high broker CPU, poor compression |
| Sweet spot | Linger of a few to a few tens of milliseconds, batch of tens to a few hundreds of KiB | Good throughput, small added delay, healthy compression |
| Too much batching | Linger in seconds, very large batches | Long delays, large memory use, spiky traffic and costly retries |
Think of a restaurant kitchen that cooks orders one at a time versus cooking in trays. Cooking each meal alone is quick for the first diner but exhausts the cooks. Waiting for a full tray makes the first diner wait too long. A kitchen that starts a tray every few minutes serves most people quickly and keeps the ovens busy. Most of the benefit arrives with a short wait, not a long one.
Ask the business for a latency budget before touching any setting. “Events must be stored within one hundred milliseconds” allows generous batching. “Within five milliseconds” does not. The budget picks the region of the curve.
14Worked Example: From Tiny Batches to Healthy Batches
A small calculation shows why linger.ms often matters more than batch.size.
A service sends ten thousand messages per second, each one kilobyte in size, to a topic with ten partitions. Keys are well distributed, so each partition receives about one thousand messages per second, which is one megabyte per second, or roughly one kilobyte per millisecond. The producer uses the default batch.size of sixteen kibibytes and a linger of zero.
With the defaults
Because linger is zero, the sender thread ships whatever is waiting the moment it is free. A request to a broker takes about two milliseconds round trip. During those two milliseconds, each partition collects about two messages. So every batch holds about two kilobytes, far below the sixteen kibibyte limit. The producer sends about five hundred requests per second per broker, each carrying tiny batches.
With a linger of ten milliseconds
Now each partition may collect messages for up to ten milliseconds. At one kilobyte per millisecond, a batch holds about ten kilobytes when the timer expires, still under the size limit. Requests to each broker drop to about one hundred per second, one fifth as many, and each batch is five times bigger. Compression improves and broker work falls. The price is at most ten milliseconds of extra delay.
With a larger batch size and linger of twenty milliseconds
With batch.size raised to sixty-four kibibytes and a linger of twenty milliseconds, a batch would grow to about twenty kilobytes before the timer fires. The size limit is not reached, so raising it made no difference. Linger was the limiting factor. This example shows the general rule: at moderate rates, linger decides the batch size, and batch.size only sets the ceiling.
| Setting combination | Approx. batch size | Requests per second per broker | Added delay |
|---|---|---|---|
| Linger 0, batch 16 KiB | About 2 KB | About 500 | Almost none |
| Linger 10 ms, batch 16 KiB | About 10 KB | About 100 | Up to 10 ms |
| Linger 20 ms, batch 64 KiB | About 20 KB | About 50 | Up to 20 ms |
| Linger 20 ms, batch 8 KiB | About 8 KB, limited by size | About 125 | Under 20 ms, since batches fill first |
These figures are simplified examples that assume even traffic and a steady round trip. Real behavior varies. Use the calculation to build intuition and then confirm with your own metrics.
15Measuring: Producer Metrics That Matter
You cannot tune what you cannot see. The producer publishes exactly the numbers you need.
| Metric | What it tells you | How to read it |
|---|---|---|
| batch-size-avg | Average size of batches in bytes | Compare with batch.size; far below means linger or traffic is limiting |
| batch-size-max | Largest batch seen | Close to batch.size means some batches are hitting the ceiling |
| records-per-request-avg | Average records in a request | Low values mean little batching |
| record-queue-time-avg | Average time a record waits in the accumulator | Approximates the delay your batching adds |
| request-rate | Requests sent per second | Should fall as batching improves |
| request-latency-avg | Round trip time of a request | Rising values show slow brokers or network |
| compression-rate-avg | Ratio of compressed to original size | Lower is better; near one means little benefit |
| bufferpool-wait-ratio | Share of time threads waited for memory | Above zero means buffer pressure |
| buffer-available-bytes | Memory not currently used | Near zero means the buffer is full |
| record-error-rate and record-retry-rate | Failures and retries | Retries multiply the cost of large batches |
Read them as a group
Small batches, high request rate
You are under-batching. Raise linger, and check partition counts and flush calls.
Batches at maximum size, high queue time
Batches fill too slowly to send, or the sender cannot keep up. Consider a larger batch.size or faster brokers.
Buffer nearly empty of free space
Memory pressure. Look at slow acknowledgments before raising buffer.memory.
Compression rate near one
Batches are too small or data is not compressible. Test with larger batches.
The producer client exposes these metrics through its built-in metrics interface and JMX. Export them to your monitoring system and chart them per producer instance.
16Broker-Side Signals
Well-tuned producers make brokers calmer. Broker metrics confirm that your tuning helped.
The broker measures how long it takes to handle each type of request and how busy its threads are. After tuning producers, look for these changes on the broker.
| Broker signal | Meaning | Expected change after better batching |
|---|---|---|
| Produce request rate | Number of produce requests per second | Falls |
| Request handler idle ratio | Share of time request threads are idle | Rises, meaning more spare capacity |
| Network processor idle ratio | Share of time network threads are idle | Rises |
| Produce request queue time | Time a request waits before being handled | Falls |
| Produce total time | Whole time to serve a produce request | May rise slightly per request, since requests are bigger |
| Bytes in per second | Data volume | Unchanged, since the same data arrives |
| CPU usage | Processor load | Falls |
Interpreting produce time
The total time of a produce request is made of parts: waiting in the queue, local processing, waiting for followers when acks is all, and sending the response. Bigger batches slightly increase the local and replication parts, since there is more data, but they reduce the queue part, because there are fewer requests. Overall, a well-tuned producer lowers the load, even if a single request takes a little longer.
Picture a customs officer at an airport. Checking one thousand separate parcels each with its own forms overwhelms the desk. Checking ten pallets, each with a single set of papers, takes each check a little longer but clears the same goods with the desk far less busy. The queue outside shrinks.
Many producers share the same brokers. One badly tuned producer sending tiny batches at a high rate can slow everyone. Watch broker metrics as a whole and find the heavy request senders when idle ratios fall.
17A Step-by-Step Tuning Method
Change one thing at a time, measure, and stop when the target is met.
Write down targets
State the required throughput, the latency budget and the resource limits. Without targets, tuning never ends.
Measure the baseline
Record batch-size-avg, records-per-request-avg, request-rate, record-queue-time and broker idle ratios under realistic load.
Set durability first and leave it
Choose acks, idempotence and replication according to business needs. Do not trade them for speed.
Raise linger.ms in small steps
Try values such as five, ten and twenty milliseconds. Watch average batch size and queue time. Stop when the gain flattens or the latency budget is near.
Raise batch.size if batches hit the ceiling
If batch-size-avg is near batch.size, double the batch size and check memory. If it is far below, leave it alone.
Choose compression
Test lz4 or zstd. Compare compression rate, producer CPU and broker load.
Check memory
Confirm that buffer.memory suits the number of active partitions and the batch size, with no time spent waiting for memory.
Test failure behavior
Restart a broker or add network delay and see how batches, retries and buffers behave.
Document and monitor
Record final settings with the reason for each, and alert when metrics drift from the measured norm.
flowchart TD
A["Set targets and measure baseline"] --> B["Change one setting"]
B --> C["Run realistic load"]
C --> D{"Targets met and metrics healthy"}
D -->|"No"| B
D -->|"Yes"| E["Test failure cases"]
E --> F["Document settings and set alerts"]
If you change linger, batch size and compression together and throughput improves, you will not know which change helped. If it worsens, you will not know which one hurt. Change one setting, then measure.
18Tuning Recipes by Workload
Different jobs need different starting points. These recipes are places to begin, not final answers.
| Workload | linger.ms | batch.size | Compression | Notes |
|---|---|---|---|---|
| Interactive, very low latency | 0 to 2 | Default | None or lz4 | Accept smaller batches; keep partitions per producer modest |
| Balanced event collection | 5 to 20 | 32 to 128 KiB | lz4 or zstd | Typical starting point for application events |
| High throughput logging or metrics | 20 to 100 | 128 to 512 KiB | zstd or lz4 | Check memory carefully across many partitions |
| Bulk load or backfill | 100 or more | 512 KiB to 1 MiB | zstd | Delay is acceptable; watch broker limits |
| Large messages | 5 to 20 | At least a few times the message size | Depends on data | Align request and broker limits |
| Very many partitions | Higher than usual | Smaller than usual | lz4 or zstd | Batches fill slowly; watch total memory |
How to use a recipe
Pick the nearest row
Choose the workload that looks most like yours as a starting point.
Test with your own data
Message size, key spread and compressibility change results, so measure with real samples.
Adjust from evidence
Use the metrics of chapter fifteen to move up or down.
Published recommendations come from particular hardware, message shapes and Kafka versions. Use them to save time on the first guess, not as proof that your system needs the same values.
19The Consumer Side: Fetch Batching
Consumers have their own version of batching. It controls how much data one fetch request brings back.
Just as producers batch messages before sending, consumers batch what they receive. A consumer sends a fetch request to a broker, and the broker replies with data from one or more partitions. Several settings shape that reply.
| Setting | Default | Meaning |
|---|---|---|
| fetch.min.bytes | 1 byte | The broker waits until at least this much data is available before answering |
| fetch.max.wait.ms | 500 ms | The longest the broker waits for fetch.min.bytes to accumulate |
| fetch.max.bytes | 50 MiB | Maximum data returned by a whole fetch response |
| max.partition.fetch.bytes | 1 MiB | Maximum data returned per partition |
| max.poll.records | 500 | Maximum records returned to your code by one poll call |
How they resemble producer settings
fetch.min.bytes and fetch.max.wait.ms
Together they work like batch.size and linger.ms: wait for enough data, but not longer than a limit.
max.partition.fetch.bytes
Bounds the amount taken per partition, like a batch ceiling.
max.poll.records
Controls how much work one loop iteration receives, which affects processing time and rebalancing.
Raising fetch.min.bytes reduces the number of fetch requests and lowers broker load, at the cost of latency up to fetch.max.wait.ms. This is a good change for consumers that process data in bulk and do not need every message the moment it arrives. On the other hand, a consumer that reads a topic with very large batches must have fetch limits big enough to hold at least one full batch, or it cannot make progress.
Think of a shopper who visits the market. Buying one apple per trip wastes time, so the shopper decides to leave the market only after collecting enough for the week, but never stays longer than an hour. Fetch min bytes is “enough for the week”, and fetch max wait is “no more than an hour”.
Well-tuned producer batches do not help if consumers fetch tiny amounts at a fast rate. Review both sides together when a topic carries heavy traffic.
20Streams, Connect and Other Frameworks
You often use producers indirectly. The same settings apply, but you must know where to set them.
Producer settings with a prefix
A Streams application creates internal producers. You pass producer settings through the Streams configuration using a producer prefix. Streams also flushes on each commit, which affects batch size, especially with a short commit interval.
Worker and connector overrides
Source connectors use producers. Settings go in the worker configuration with a producer prefix, and connectors can override them if the worker allows overrides.
Framework properties
Application frameworks expose Kafka producer properties under their own names. Find the mapping in their documentation and check what defaults the framework itself sets.
Agent settings
Agents that ship logs to Kafka often have their own batching layer on top of the producer. Tune both layers so they do not fight each other.
The commit interval effect in Kafka Streams
Kafka Streams flushes its producers whenever it commits progress. With exactly-once processing, the commit interval defaults to a tenth of a second, which means batches are forced out at least ten times per second regardless of linger settings. Very small commit intervals therefore limit batch size. If your Streams application shows tiny batches and you can accept a slightly longer delay in results, raising the commit interval is the effective lever.
Any code that calls flush after every message, or every few messages, turns batching off. It is common in wrapper libraries and tests. Search your code for flush calls before spending time on tuning.
21Sizing Producer Memory
A simple estimate shows whether your batch settings fit inside the memory you have given the producer.
Memory used by open batches is roughly the number of partitions the producer writes to actively, multiplied by batch.size, multiplied by the number of batches per partition that may be waiting or in flight at once. To this add space for compressed copies and for the requests that are being sent. A generous rule is to allow two to three times batch.size per active partition.
Count active partitions
How many partitions does this producer instance write to in a typical minute?
Multiply by batch.size
This gives the memory for one open batch on each active partition.
Allow for in-flight batches
Multiply by two or three so that sent batches waiting for acknowledgment do not block new ones.
Compare with buffer.memory
If the estimate exceeds the buffer, reduce batch.size, reduce partitions per producer, or raise the buffer.
An example
A producer instance writes to 120 partitions with a batch.size of 128 kibibytes. One open batch on each partition takes 120 times 128 kibibytes, which is about 15 mebibytes. Allowing three times that for in-flight batches gives about 45 mebibytes. The default buffer of 32 mebibytes is too small, so the producer would spend time waiting for memory. Either raise the buffer to 64 mebibytes, or reduce the batch size to 64 kibibytes, which brings the estimate to about 22 mebibytes.
| Input | Value | Result |
|---|---|---|
| Active partitions | 120 | |
| batch.size | 128 KiB | 15 MiB for open batches |
| In-flight allowance | Three times | About 45 MiB needed |
| Default buffer.memory | 32 MiB | Too small; memory waits appear |
| Fix option A | buffer.memory 64 MiB | Fits with room |
| Fix option B | batch.size 64 KiB | About 22 MiB; fits in the default |
The producer’s buffer lives in the application’s memory. Make sure the application has enough heap for the buffer plus its own work, or long garbage collection pauses will appear as latency spikes.
22Advantages, Disadvantages and Trade-offs
Batching is one of the best bargains in Kafka, but it is not free. Know both sides.
Advantages of good batching
- Far fewer network requests, so less overhead per message.
- Lower CPU use on producers and brokers.
- Better compression, which saves network and disk.
- Higher total throughput from the same hardware.
- Calmer brokers, leaving room for other clients.
- Lower cost on cloud networks that charge by traffic.
Disadvantages and risks
- Added waiting time for messages in a slow stream.
- More memory used by producers.
- Larger loss when a producer crashes, because unsent messages disappear with it.
- Bigger retries after a failure, since a whole batch is sent again.
- One bad record can affect the rest of its batch.
- More settings that must agree with each other.
Trade-offs in plain words
| You gain | You pay | When it is worth it |
|---|---|---|
| Throughput | Latency | When a few milliseconds do not matter to the user |
| Compression | CPU and small delay | When data is text-like and traffic is large |
| Fewer requests | Memory | When the application has memory to spare |
| Simplicity of defaults | Lower efficiency | When traffic is small and not worth tuning |
A moving company that packs one big truck is cheaper per box, but if the truck breaks down, more boxes are delayed at once. Smaller trucks cost more per box, yet each failure hurts less. Choosing the truck size is choosing between efficiency and the size of each loss.
23Performance and Scalability
Batching decides how far one producer, and one cluster, can scale.
Each producer instance has a maximum request rate it can sustain before its sender thread or network becomes the limit. Bigger batches lift that limit because each request carries more work. When you add more producer instances, though, each one sends its own small batches unless it too is tuned. Ten instances with poor batching can produce ten times the request load of one.
Fewer, busier producers
A single producer sharing one buffer usually forms better batches than many small producers. Reuse the producer object across threads.
Watch the spread
More partitions mean each gets fewer messages per second and batches fill slower. Add partitions with awareness of this effect.
Request handling capacity
Brokers spend effort per request. Fewer, larger requests stretch broker capacity further.
Compression helps
Compressed batches lower bandwidth, which matters between data centers and clouds.
The producer is safe to use from many threads. Creating one per thread or per message splits traffic into many small buffers and wastes the benefit of batching.
24High Availability and Reliability
Batching interacts with failures. Understand what happens to a batch when things go wrong.
Retries
When a request fails with a temporary error, the producer retries the whole batch. With idempotence enabled, the broker recognizes repeated batches and discards duplicates, so retries are safe. Without idempotence, a retry after a lost acknowledgment may store the batch twice. Large batches make a duplicate larger, which is another reason to keep idempotence on.
Timeouts
The delivery timeout covers everything from the moment a record is added until it is acknowledged, including the time spent waiting in the buffer. If linger, retries and broker delays together exceed that limit, records fail. Keep the delivery timeout larger than linger plus the request timeout.
Batch splitting
If a batch turns out to be too large for the broker to accept, the producer may split it into smaller ones and try again. This wastes work. Metrics for batch splits, when present, are a hint that size limits are not aligned.
Producer crashes
Records still sitting in the buffer are lost if the application dies. Larger linger times mean more records at risk. Applications that cannot lose data should call flush during a controlled shutdown, and should wait for acknowledgments before treating work as done.
flowchart TD
A["Batch sent to leader"] --> B{"Acknowledged in time"}
B -->|"Yes"| C["Records marked done"]
B -->|"No, temporary error"| D["Retry same batch"]
D --> E{"Idempotence on"}
E -->|"Yes"| F["Broker drops duplicates"]
E -->|"No"| G["Possible duplicate batch"]
D --> H{"Delivery timeout reached"}
H -->|"Yes"| I["Records fail and error is reported"]
Setting acks to zero or one to make batches finish faster increases the chance of lost data. Choose durability first, then tune batching within that choice.
25Security and Governance
Batch settings look like performance knobs, but they also touch security and fair use.
Protect the cluster
Brokers can limit how much data or request time each client may use. A producer that sends too aggressively will be slowed down, and the throttle time appears in its metrics.
Cost of TLS
Encrypted connections add CPU work for each request. Fewer, larger requests reduce that cost per message.
Who may produce
Access control lists decide which identities may write to which topics. Tuning does not replace them.
Review changes
Producer settings live in application code or files. Treat them like other production configuration, with review and version history.
Limit oversized requests
Broker size limits stop a client from sending enormous requests that strain memory.
Compression and secrets
Compression before encryption can leak information in rare designs when attackers control part of the data. Keep this in mind for sensitive payloads.
Publish approved producer profiles, such as “low latency” and “high throughput”, so teams pick a tested set of values instead of inventing their own.
26Monitoring, Logging and Alerting
Turn the metrics of chapter fifteen into dashboards and alerts that catch problems early.
| Alert | Condition (example) | Likely cause |
|---|---|---|
| Batches too small | batch-size-avg falls far below its normal range | Traffic drop, flush calls added, partition count change |
| Delay too high | record-queue-time-avg exceeds the latency budget | Linger raised, sender saturated |
| Memory pressure | bufferpool-wait-ratio above zero for minutes | Slow brokers or undersized buffer |
| Request latency rising | request-latency-avg well above baseline | Broker overload or network trouble |
| Throttling | produce-throttle-time above zero | Client quota reached |
| Errors | record-error-rate above zero | Timeouts, size limits, authorization |
Logging
Producer logs at the warning level report timeouts, expired batches and disconnections. Send them to a central place and search them during incidents. Log the effective configuration at startup, so you can see what values a running instance actually uses, rather than what you believe you set.
A car dashboard shows speed, fuel and engine heat. You do not stare at it all the time, but warning lights tell you when to look. Metrics are the dials, alerts are the warning lights.
A sudden halving of average batch size is a sign of trouble even if no fixed limit is crossed. Compare with the same time last week where possible.
27Failure Scenarios and How to Read Them
When something breaks, batch metrics often show the first clue.
| Symptom | Possible cause | What to check | Typical fix |
|---|---|---|---|
| Timeouts expiring batches | Broker slow, delivery timeout too short | request-latency, broker load, timeout values | Relieve brokers; align timeouts with linger |
| Send calls blocking | Buffer full | bufferpool-wait-ratio, available bytes | Fix slow acknowledgments; resize buffer |
| Record too large error | Record above a size limit | max.request.size and broker limits | Align limits or shrink records |
| High CPU on producer | Heavy compression or tiny batches | Compression rate, batch size | Change codec or raise linger |
| Latency spikes at low traffic | Linger too long for slow streams | record-queue-time | Lower linger for this producer |
| Uneven partition load | Hot key | Per-partition traffic | Improve key choice |
| Throughput drops after upgrade | Default values changed | Effective config log | Set values explicitly |
flowchart TD
A["Producer slow or failing"] --> B{"Send calls blocking"}
B -->|"Yes"| C["Check buffer and acknowledgment speed"]
B -->|"No"| D{"Batches expiring"}
D -->|"Yes"| E["Check broker health and timeouts"]
D -->|"No"| F{"Batches tiny"}
F -->|"Yes"| G["Check linger, flush calls, partitions"]
F -->|"No"| H["Check compression and CPU"]
28Testing and Benchmarking
Measure with realistic tests before changing production.
Kafka ships with a producer performance test tool. It generates messages of a chosen size at a chosen rate and reports throughput and latency percentiles. You pass the same producer settings you plan to use. It is useful for comparing settings side by side on a test cluster.
Use a similar cluster
Match broker count, replication factor, disks and network as closely as you can.
Use realistic messages
Real payloads compress differently from random bytes. Use samples of your own data where possible.
Warm up first
Ignore the first seconds while connections and caches settle.
Run each setting several times
Single runs can mislead. Compare averages and high percentiles.
Look at tail latency
The slowest one percent of messages often matters more than the average.
Test at rates you expect and at peaks
Settings good at peak may be poor at quiet times, and the reverse.
A test that sends as fast as possible fills batches instantly, hiding the effect of linger. Test also at fixed realistic rates.
29Design Patterns and Anti-patterns
Patterns that repeatedly work, and mistakes that repeatedly cause trouble.
Shared, long-lived producer
Context: Many threads in one application send messages.
Decision: Create one producer for the application and share it.
Consequence: Batches fill from all threads, and connections are reused.
Small linger plus compression
Context: Moderate traffic and a latency budget of tens of milliseconds.
Decision: Set a linger of a few milliseconds and use lz4 or zstd.
Consequence: Most of the batching gain with a small delay.
Separate producers by workload
Context: One application sends both urgent alerts and bulk logs.
Decision: Use two producers with different settings.
Consequence: Urgent messages are not delayed by bulk-oriented linger.
Producer per message
Problem: Creating and closing a producer for each message.
Effect: No batching, constant connection setup, heavy load.
Better: Keep one producer alive.
Flush after every send
Problem: Calling flush to make sure each message leaves at once.
Effect: Batches contain one record.
Better: Use callbacks and let batches form.
Huge batch size with no linger
Problem: Raising batch.size alone and expecting bigger batches.
Effect: Memory is reserved but batches stay small.
Better: Raise linger too, or fix the real limit.
30Best Practices and Common Mistakes
A short checklist to keep tuning sane.
Best practices
- Start from measurements, not from advice on the internet.
- Set a latency budget before tuning.
- Change one setting at a time.
- Enable idempotence and choose acks for durability first.
- Use lz4 or zstd unless you have a reason not to.
- Share one producer across threads.
- Align size limits on producer, broker and topic.
- Log effective configuration and chart batch metrics.
- Set important values explicitly, so upgrades do not surprise you.
Common mistakes
- Raising batch.size and ignoring linger.
- Calling flush after every message.
- Creating a producer for every request.
- Trading durability for speed without a decision.
- Copying settings from a different workload.
- Forgetting the memory cost of many partitions.
- Ignoring consumer fetch settings.
- Testing only with unlimited send rates.
- Never checking metrics after the change.
A modest linger and a good codec solve most cases. Reach for large values only when metrics show they are needed.
31Real-World Examples
These are illustrations of common situations, not reports about specific companies.
Clickstream collection for a website
A website sends a message for every click. Thousands of small events arrive per second. A linger of about ten to twenty milliseconds, a batch size of several tens of kibibytes and lz4 compression cut requests sharply. A delay of twenty milliseconds is invisible to analysts reading dashboards.
Payment confirmation events
A payment service must publish an event before responding to a customer. Here the latency budget is strict, so linger stays near zero and acks is all with idempotence on. Batching gains are modest, but volume is low, so this is fine.
Log shipping from many servers
Thousands of servers ship logs. Each agent has low volume, so batches fill slowly. Raising linger to a few hundred milliseconds and compressing the batches saves large amounts of bandwidth, and logs are read later, so the delay is acceptable.
Nightly data backfill
A migration job copies millions of old records into a topic. Nobody waits for individual messages. Large batches, a long linger and zstd compression finish the job faster and with less load on the cluster.
Sensor readings from devices
Small readings arrive from a gateway that serves many devices. Messages are tiny, so batch size matters less than linger. A short linger groups hundreds of readings per batch with little delay.
32Glossary
Plain meanings for the words used in this guide.
| Term | Meaning |
|---|---|
| Producer | A program or library that sends messages to Kafka. |
| Broker | A Kafka server that stores messages. |
| Topic | A named stream of messages. |
| Partition | One ordered slice of a topic. |
| Record | One message with key, value and metadata. |
| Batch | A group of records for one partition sent together. |
| Request | One network call carrying batches for one broker. |
| Record accumulator | The producer’s in-memory area where batches form. |
| Sender thread | The background worker that ships batches to brokers. |
| batch.size | Maximum size in bytes of a batch for one partition. |
| linger.ms | How long the producer waits for more records before sending. |
| buffer.memory | Total memory the producer may use for unsent records. |
| Backpressure | Slowing down senders when a buffer is full. |
| Compression | Shrinking data to save space and bandwidth. |
| Idempotence | Protection against duplicate writes when retrying. |
| Acknowledgment (acks) | How many brokers must confirm a write. |
| Throughput | Amount of data handled per second. |
| Latency | Time taken for one message to be stored. |
| Quota | A limit on how much a client may use. |
33Frequently Asked Questions
Short answers to questions that come up again and again.
No. It only raises the ceiling. If batches never reach the ceiling, a bigger value wastes memory and changes nothing.
It depends on your latency budget. A few to a few tens of milliseconds is a common starting range. Measure and adjust.
Within one partition, order is kept, provided idempotence is on or in-flight requests are limited. Across partitions, there is no global order anyway.
Not by itself. Unsent records in memory are lost if the application crashes, so flush during shutdown and use acknowledgments properly.
Common reasons are linger at zero with low traffic, many partitions, flush calls, or a producer created per message.
lz4 is fast and light on CPU. zstd usually compresses better. Test on your data.
Per batch. That is why larger batches compress better.
For records without keys, it fills one partition’s batch before moving to another, which produces fuller batches than spreading each record across partitions.
They can. Check the documentation for your version and set important values explicitly.
Yes, when traffic is heavy. Fetch settings act like consumer batching and affect broker load.
Average batch size rises, request rate falls, broker idle ratios improve, and latency stays within budget.
Keep linger near zero, use light or no compression, and accept smaller batches. Give latency-critical messages their own producer.
Yes, and it should. Sharing improves batching and saves connections.
Usually not. If only a few records arrive each second, waiting rarely adds a second record to the batch. You add delay and gain almost nothing. Batching pays off when many records arrive within the waiting window, so measure the arrival rate per partition before deciding.
It can mask the problem for a while. Buffers absorb short slowdowns, so senders see no delay at first. When the buffer fills, calls to send begin to block and errors follow. Watch request latency and buffer metrics together, so a slow broker is noticed before the buffer runs out.
Review them whenever traffic changes a lot, partition counts change, message sizes change, or you upgrade Kafka clients. Settings that were ideal for last year’s traffic may be wasteful or too cautious today. A short review each quarter, guided by the dashboard, is usually enough.
34Summary and Key Takeaways
Batch size tuning is the craft of balancing waiting time against efficiency, one measured step at a time.
Kafka producers gather records into batches per partition and send them to brokers together. Batch size, linger, compression and memory decide how full those batches are. Good tuning lowers the load on brokers and networks while keeping delay within what the business accepts. It starts with a latency budget, proceeds with careful measurement, and ends with monitoring that keeps the result healthy.
Key Takeaways
- A batch is a group of records for one partition; batch.size is a ceiling, not a target.
- linger.ms is often the more powerful lever, because it lets batches fill before sending.
- Compression works per batch, so bigger batches compress better.
- Buffer memory, partition count and batch size must fit together, or the producer blocks.
- Choose durability settings first, then tune batching within them.
- Change one setting at a time and watch batch-size-avg, queue time and broker idle ratios.
- Consumers have their own fetch batching; tune both ends of the pipe.