CMD Guide
HomeSystem DesignKafka

Scalability and Performance (2)

A single Kafka broker sustains hundreds of MB/s not by making each message fast but by amortizing the fixed per-message costs away: the producer packs hundreds of records into one batch, compresses that whole batch as a unit, the broker appends it sequentially to the OS page cache untouched, and on the way out ships those exact bytes to consumers with one sendfile() syscall that never copies them through the JVM.

Partitioning and consumer groups give Kafka horizontal scale — that mechanism is covered in Kafka Internals. This page is the other half: what makes a single broker/partition so fast. Three levers do the work — batching, compression, and zero-copy — and they interact.

1. Batching: amortize the fixed cost per record

Every produce request carries fixed overhead independent of payload size: a TCP round trip, request framing, a broker network-thread handoff, an offset/index update, and an ack. Sending one record per request pays that overhead one million times for one million records. Batching pays it once per batch.

The producer accumulates records per partition in an in-memory buffer and flushes the batch when either trigger fires first:

The default linger.ms=0 means "send as soon as the network is free" — under load it still batches opportunistically, but under a trickle it sends tiny batches. Raising it to 5–20 ms deliberately waits to build bigger batches: you trade a few ms of produce latency for a large drop in request count and much better compression.

2. A worked example

Ingest 1,000,000 events, each a ~100-byte JSON record, arriving at 200k/s. Records carry ~14 bytes of batch overhead each, so a 16 KB batch holds ≈ 16384 / 114 ≈ 140 records.

MetricA: linger.ms=0, no batching, no compressionB: linger.ms=10, batch.size=16384, snappy
Records per request1~140
Produce requests for 1M records1,000,000~7,150
Per-request overhead paid1,000,000×~7,150× (≈ 140× less)
Bytes appended & sent (payload)~114 MB~38 MB (snappy ≈ 3× on repetitive JSON → ~66% less network + disk)
Added produce latency (worst case)0≤ 10 ms (a record that lands in an empty buffer)

Compression runs on the whole batch, so the repeated JSON field names across those 140 records collapse into one dictionary — this is exactly why Kafka compresses batches, not individual messages: cross-record redundancy is where the ratio comes from.

diagram
diagram

3. Compression: pick the codec by your bottleneck

Set on the producer via compression.type; the broker default (compression.type=producer) keeps the producer's codec and stores the batch as received — which is what preserves zero-copy on the read path. The codec is a CPU-vs-bytes trade:

CodecRatio (typical JSON)CPUUse when
nonenonePayload already compressed (images/video), or ultra-low latency
snappy~2–3×lowCPU-tight or latency-sensitive; good default
lz4~2–3×lowSame class as snappy, often slightly faster
gzip~4–5×highBytes/storage dominate and CPU is spare; slowest
zstd~4–5×moderateBest all-rounder (since Kafka 2.1): near-gzip ratio at far lower CPU

4. Why the read path is nearly free

Two OS-level tricks, not Kafka code, carry most of the throughput:

Pitfalls

Delivery honesty: idempotent producer ≠ EOS

KnobWhat it actually guaranteesWhat it does NOT
enable.idempotence=trueProducer retries do not create duplicate records in a partition (same PID/epoch/seq)Consumer-side EOS; cross-partition atomicity; "my email sent once"
acks=all + min ISRCommitted data survives f-1 broker loss in the ISRZero consumer duplicates after rebalance
Kafka transactions (EOS)Atomic consume-process-produce within the transactional APIIdempotency of external side effects (HTTP, SQL without keys)

When NOT to chase EOS: most task pipelines want at-least-once + idempotent handlers. Full transactions cost latency and operational complexity; only pay when the business requires read-process-write atomicity on Kafka itself.

Operability signals

When to lean on this — and when not

These are tuning strategies, so decide by what dominates your workload.

Versus a push broker (RabbitMQ). RabbitMQ pushes messages individually, acks per message, and typically holds them in memory — you gain flexible routing, per-message priority/TTL, and low fan-out latency, but you cannot amortize per-message overhead the way Kafka's log does, so you won't reach Kafka's per-broker MB/s at the same CPU. Choose Kafka's log + batch + zero-copy model when volume is high and consumers pull streams they may replay; prefer RabbitMQ when per-message routing, priority, or the lowest single-message latency matter more than raw throughput.

Takeaways


Re-authored and deepened for this guide, repositioned to Kafka's per-broker throughput mechanism (partitions/consumer-groups live in the Kafka Internals page). Sources: Apache Kafka documentation (producer configs — batch.size, linger.ms, compression.type; message format); Jay Kreps, "The Log" and the LinkedIn Kafka paper; Narkhede, Shapira & Palino, Kafka: The Definitive Guide; Linux sendfile(2) / Java NIO FileChannel.transferTo and the classic "Efficient data transfer through zero copy" write-up.

🤖 Don't fully get this? Learn it with Claude

Stuck on Scalability and Performance (2)? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Scalability and Performance (2)** (System Design) and want to truly understand it. Explain Scalability and Performance (2) from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Scalability and Performance (2)** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Scalability and Performance (2)** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Scalability and Performance (2)** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes