如果 Producer 每条消息都单独发一次网络请求,吞吐量会被请求数量本身卡死。Kafka 客户端会把发往同一个分区的消息攒成一个 RecordBatch 再一起发送。这次对比了"几乎不攒批"和"正常攒批"两种配置,用耗时把差距量化出来。
命令 · java // 配置A:batch.size=1(事实上让每条消息都单独成批,等价于不攒批) producer.send(...) x 20000 // 每条消息都发一次请求 // 配置B:batch.size=65536, linger.ms=20(正常攒批配置) producer.send(...) x 20000 // 消息攒批后打包发送
no-batch(batch.size=1) elapsed_ms=11730 for 20000 records batched(batch.size=65536,linger=20ms) elapsed_ms=77 for 20000 records speedup=152.3x
同样是发 2 万条 100 字节的消息、同样是 acks=all、同样是的 3 节点集群,只改了 batch.size 和 linger.ms 两个参数:几乎不攒批时耗时 11730ms,正常攒批时只要 77ms,测出 152 倍的速度差。这不是因为网络变快了,而是因为攒批把 2 万次独立的网络往返压缩成了少数几次批量请求——每次请求的固定开销(连接调度、请求排队、Broker 端处理请求头)被分摊到了一批消息上,而不是每条消息都要单独承担一次。
RecordBatch 存在的意义就是测出来的这 152 倍差距:不攒批的话,Kafka 的吞吐量会被"每条消息一次网络请求"这个模式直接限制死,跟消息大小、磁盘速度都没关系,纯粹是请求数量本身的开销。这也是为什么 Kafka 官方不建议为了"降低单条消息延迟"就把 batch.size 调到 1 或者 linger.ms 调到 0——代价可能是上百倍的吞吐损失。
这里用 6 条消息做示意(测量用的是 20000 条,见上一节的耗时数据),机制完全一致,只是把规模缩小到方便逐步回放。