4、Broker核心参数调优:网络线程模型、日志段管理、刷盘策略、副本同步机制

Broker 是 Kafka 的命根子。你集群稳不稳,吞吐高不高,全看 Broker 配得怎么样。

很多人上来就改 num.partitions,改 log.retention.hours,觉得这就够了。其实不然。真正决定性能的,是网络线程模型怎么搭、日志段怎么切、刷盘怎么控、副本怎么同步。这几个点没搞透,集群早晚出问题。

我见过不少团队,Broker 参数抄来抄去,出了事才回头翻文档。今天咱们就把这四块掰开揉碎,讲清楚。

核心观点:Broker 调优不是调单个参数,而是调一套协作机制。网络、存储、一致性,三者必须平衡。

Broker 核心参数调优知识体系 Broker 调优 网络线程模型 日志段管理 刷盘策略 副本同步机制 num.network.threads num.io.threads queued.max.requests log.segment.bytes log.roll.ms log.retention.bytes flush.messages flush.ms log.flush.interval replica.lag.time.max.ms min.insync.replicas unclean.leader.election

4.1 网络线程模型:别让网络成为瓶颈

Kafka 的网络层用的是 Reactor 模式。说白了,就是有一组线程专门收请求,另一组线程专门处理请求。

两个核心参数:

  • num.network.threads:网络线程数,默认 3。负责从 socket 读取请求,放到请求队列里。
  • num.io.threads:IO 线程数,默认 8。从队列里拿请求,真正干活——写磁盘、读磁盘、处理元数据。

我遇到过一个问题:某次压测,吞吐死活上不去。CPU 才用了 30%,但网络线程全忙。后来发现 num.network.threads 设成了 2,而机器是 32 核的。改成 8 之后,吞吐直接翻倍。

我的建议:网络线程数可以设为核心数的 1/4 到 1/2。IO 线程数设为核心数的 1 到 2 倍。别盲目设大,线程切换也有开销。

还有一个参数容易被忽略:queued.max.requests。它控制请求队列的长度。默认 500。如果生产端发送速度远大于消费端处理速度,队列会积压。这时候要么扩容,要么限流。

注意:不要把 queued.max.requests 设得太大。队列太长,延迟会飙升。我曾经见过有人设成 10000,结果一个慢请求堵住,后面所有请求都等超时。

4.2 日志段管理:文件切分与清理

Kafka 的日志不是写在一个大文件里。它按段(Segment)来切。每个分区目录下,有一堆 .log、.index、.timeindex 文件。

为什么切段?两个原因:

  • 方便清理过期数据——直接删整个段文件,比逐条删快得多。
  • 方便索引——每个段有自己的偏移量索引,查找时只扫一个段。

关键参数:

参数 默认值 说明
log.segment.bytes 1GB (1073741824) 单个日志段的最大大小。达到这个值就滚动新段。
log.roll.ms 7天 (604800000) 即使没写满,超过这个时间也强制滚动。
log.retention.bytes -1(不限) 分区总大小上限。超过就删最老的段。
log.retention.hours 168(7天) 数据保留时间。超过就删。

我个人的习惯:日志段不要太大,也不要太小。1GB 对大多数场景合适。如果你消息体很小(比如几百字节),可以设小一点,比如 256MB。这样滚动更频繁,清理更及时。

但注意:段太小会导致文件数量爆炸。每个分区几千个段文件,文件系统 metadata 会成负担。

经验之谈:如果 Topic 的 TPS 很高,建议把 log.roll.ms 设短一些,比如 1 小时。这样即使某个段没写满,也会按时滚动。避免单个段跨度过大,影响清理效率。

4.3 刷盘策略:数据安全与性能的博弈

Kafka 写数据,是先写 PageCache,再异步刷到磁盘。这样快,但风险也大——如果机器断电,PageCache 里的数据就丢了。

刷盘参数:

  • flush.messages:累计多少条消息后强制刷盘。默认 Long.MAX_VALUE,几乎不主动刷。
  • flush.ms:每隔多少毫秒强制刷盘。默认也是 Long.MAX_VALUE。

你看,Kafka 默认是「能不刷就不刷」。为什么?因为刷盘是磁盘 IO 操作,很慢。频繁刷盘会严重降低吞吐。

但如果你对数据安全要求极高,比如金融交易场景,那就得牺牲一点性能。

我的建议:大多数场景下,保持默认就好。操作系统自己会刷 PageCache。如果你实在不放心,可以设 flush.ms=5000,每 5 秒刷一次。别设成 1 秒,磁盘受不了。

另外,别忘了 log.flush.interval 系列参数。它们控制的是「日志管理器」的刷盘行为,跟上面说的不太一样。简单说:flush.messages 是全局的,log.flush.interval 是 per-topic 的。我一般只调全局的,per-topic 的保持默认。

避坑:我曾经在一个项目里,把 flush.messages 设成 1,结果生产环境磁盘 IO 直接打满,吞吐掉到原来的十分之一。后来改成 10000,才恢复正常。刷盘频率一定要跟你的硬件能力匹配。

4.4 副本同步机制:ISR 与 Leader 选举

副本同步是 Kafka 高可用的基石。但很多人对 ISR(In-Sync Replicas)的理解只停留在「同步副本列表」这个层面。

ISR 是怎么维护的?

  • Follower 定期向 Leader 发送 Fetch 请求,拉取最新数据。
  • 如果 Follower 在 replica.lag.time.max.ms(默认 30 秒)内没有跟上 Leader 的进度,就会被踢出 ISR。
  • 被踢出的 Follower 如果追上了,可以重新加入 ISR。

这里有个关键参数:min.insync.replicas。它决定了「至少要有多少个 ISR 副本,才允许写入」。默认是 1,也就是说只要 Leader 活着就能写。

但如果你设成 2,而 ISR 里只有 Leader 自己,那写入就会报错——NotEnoughReplicasException。

生产建议:对于重要数据,把 min.insync.replicas 设为 2,同时副本因子设为 3。这样即使一个 Broker 挂了,还能保证至少两个副本同步,数据不会丢。

再说 Leader 选举。Kafka 的 Leader 选举有两种:

  • Clean 选举:从 ISR 里选一个。数据完整,不会丢。
  • Unclean 选举:允许从非 ISR 的副本里选。数据可能丢,但服务可用。

控制这个行为的参数是 unclean.leader.election.enable。默认 false,也就是只允许 Clean 选举。

我的建议:除非你明确知道自己在做什么,否则保持 unclean.leader.election.enable=false。数据一致性比可用性重要。我曾经见过有人为了「高可用」把这个参数打开,结果 Leader 挂了之后,选了一个落后几万条数据的 Follower 当 Leader,大量消息丢失,业务方直接炸了。

最后提一下 replica.lag.time.max.ms。这个值设得太小,网络抖动就会导致 Follower 频繁进出 ISR,引发不必要的副本重平衡。设得太大,ISR 里可能包含已经落后很多的副本,影响数据安全。

我一般设成 10 到 30 秒。如果网络环境好,可以设短一点;如果跨机房,建议设长一点。

小技巧:监控 kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions 这个指标。如果它长期不为 0,说明有副本同步跟不上,需要排查网络或磁盘 IO。

好了,Broker 调优的四个核心维度就讲到这里。网络线程模型管并发,日志段管理管存储,刷盘策略管持久化,副本同步管一致性。每个维度都有取舍,没有银弹。关键是根据你的业务场景,找到那个平衡点。