<p><strong>2025 年 3 月 18 日,Apache Kafka 4.0 正式发布。</strong> 在此次版本更新中,相较于架构层面的升级,开发者们也应关注一个关键的细节变更:<strong>官方将生产者参数 <code>linger.ms 的默认值,从沿用多年的 0ms 正式修改为 5ms。
这一调整直击传统性能调优的认知盲区,在传统观念中,linger.ms=0 意味着”零等待”和实时发送,通常被视为降低延迟的首选策略。然而,Kafka 4.0 的默认值变更揭示了一个更深层的性能逻辑:在复杂的网络 I/O 模型中,单纯追求发送端的实时性并不等同于全局的低延迟。通过引入微小的”人工延迟”来换取更高的批处理效率,往往能显著降低系统的延迟。
以 Kafka 4.0 的默认值变更为契机,本文将深入分析 linger.ms 和 batch.size 这两个核心参数背后的协同机制。帮助你在面对复杂的生产环境时,基于原理掌握linger.ms 和 batch.size 的最佳实践。
概念拆解:linger.ms 和 batch.size 参数
为了透彻理解这次变更背后的深层逻辑,首先我们需要回归基础,准确理解这两个核心参数的概念。
linger.ms:
生产者会将两次请求传输之间到达的所有记录组合成单一的批处理请求。这种攒批行为通常在记录到达速率超过发送速率的高负载场景下自然发生,但在负载适中时,客户端也可通过配置 linger.ms 引入少量的”人为延迟”来主动减少请求数量。其行为逻辑类似于 TCP 协议中的 Nagle 算法:生产者不再立即发送每一条到达的记录,而是等待一段指定的时间以聚合更多后续记录。该设置定义了批处理的时间上限,发送行为遵循”先满足者优先”原则——一旦分区积累的数据量达到 batch.size,无论 linger.ms 是否到期,批次都会立即发送;反之,若数据量不足,生产者将”逗留”指定时长以等待更多记录。在 Apache Kafka 4.0 中,该参数的默认值已从 0ms 调整为 5ms,其依据在于更大批次带来的效率增益通常足以抵消引入的等待时间,从而实现持平甚至更低的整体生产者延迟。
batch.size:
当多条记录需发往同一分区时,生产者会将这些记录聚合为批次(Batch)以减少网络请求频率,从而优化客户端与服务端的 I/O 性能。batch.size 参数定义了该批次的默认容量上限(以字节为单位),超过该阈值的单条记录将不被纳入批处理逻辑。发往 Broker 的单个请求通常包含多个批次,分别对应不同的分区。配置过小的 batch.size 会限制批处理的发生频率并可能降低吞吐量(设置为 0 将完全禁用批处理);而过大的配置则可能因生产者总是基于此阈值预分配缓冲区而导致内存资源的轻微浪费。该设置确立了发送行为的空间上限:若当前分区积累的数据量未达到此阈值,生产者将依据 linger.ms(默认为 5ms)的设定进行等待;发送触发逻辑遵循”先满足者优先(Whichever happens first)”原则,即一旦数据量填满缓冲区或等待时间耗尽,批次即会被发送。需要注意的是,Broker 端的背压可能导致实际的有效等待时间超过配置值。
通过对两个维度的拆解,我们可以清晰地看到 linger.ms 和 batch.size 的协同工作模式:
- 它们共同决定了 RecordBatch(批次)的大小和 ProduceRequest(请求)的发送时机。
linger.ms和batch.size参数值较大 ->RecordBatch 和 ProduceRequest 批处理效果越好 -> Kafka 服务器需要处理的 RPC 数量更少 -> Kafka 服务端 CPU 消耗越低。- 副作用:客户端在批处理上花费的时间增加,从而导致客户端的发送延迟变高。
这引出了一个关键的性能权衡问题:
“在服务端 CPU 资源充足的前提下,为了追求极致的低延迟,是否应当尽可能最小化
linger.ms和batch.size?”
基于直觉的推断,答案似乎是肯定的。然而,Kafka 4.0 的官方文档指出了相反的结论:
“Apache Kafka 4.0 将默认值从 0 调整为 5。尽管增加了人为的等待时间,但更大批次带来的处理效率提升,通常会导致相似甚至更低的生产者延迟。”
linger.ms=0 代表即时发送,为什么在延迟的表现上反而不如”先等待 5ms”?
核心原理:Kafka 服务端与客户端交互的底层规则
要透彻理解这一反直觉的性能表现,我们不能仅停留在客户端配置的表面,而必须深入 Apache Kafka 网络协议的底层。延迟的产生,本质上源于客户端发送策略与服务端处理模型之间的交互机制。为了探究其根源,我们需要分别从服务端和客户端两个维度,解析这套底层规则的运作逻辑。
1. 服务端视角:严格按序的”串行”模式
Kafka 的网络协议在设计上与 HTTP 1.x 颇为相似,它采用的是一种严格的顺序且串行的工作模式。这是理解所有延迟问题的基石:
- 顺序性(Sequential): 对于来自同一个 TCP 连接的请求,服务端必须严格按照接收到的顺序进行处理,并按同样的顺序返回响应。
- 串行性(Serial): 服务端只有在完全处理完当前请求并发送响应后,才会开始处理下一个请求。这意味着,即便客户端并发发送了 N 个 ProduceRequest,服务端也会严格执行’One-by-One’策略:必须等到前一个请求的数据完成所有 ISR 副本同步并返回响应后,才会开始处理下一个请求。
这意味着: 哪怕客户端一股脑地并发发送了 N 个 ProduceRequest,服务端也不会并行处理。如果前一个请求因为 ISR 同步卡顿了,后续的所有请求都只能在服务端排队等候。

2. 客户端视角:化解拥堵的”Batch”原理
在客户端侧,Producer 的批处理主要包含两个核心模块:RecordAccumulator 和 Sender,分别对应 RecordBatch 和 ProduceRequest。
- RecordAccumulator :负责将 RecordBatch 进行批处理。
KafkaProducer#send将记录放入 RecordAccumulator 进行批处理。当分区内的 ProduceBatch 数据超过batch.size时,它会切换到下一个分区并创建一个新的 ProduceBatch 进行批处理。 - Sender:负责维护与服务器节点的连接并分批发送数据。它会基于节点从 RecordAccumulator 中排干就绪分区的数据,将它们打包成 ProduceRequest 并发送。排干需要同时满足以下条件:
- 连接上的在途请求数量小于
max.in.flight.requests.per.connection=5。 - 对应节点的任何 ProduceBatch 超过
linger.ms或超过batch.size
- 连接上的在途请求数量小于