Kafka消费端越堆越多,业务侧看到的数据延迟从秒级拖到小时级,broker的CPU和磁盘却都很闲。这种状态问题基本都在消费组自己身上,排查的入口只有一个:把每个分区的LAG拉出来看。
先把每个分区的LAG拉出来
kafka-consumer-groups.sh是唯一需要记住的命令,它跟Kafka二进制包一起发布,在bin目录里:
bin/kafka-consumer-groups.sh --bootstrap-server 10.0.0.11:9092 --describe --group order-consumer
输出看三列:CURRENT-OFFSET是消费到哪一条,LOG-END-OFFSET是分区当前末尾,LAG就是两者之差。LAG为0的分区不用管,剩下的按LAG从大到小排。
堆积往往集中在少数几个分区,而不是均匀摊在所有分区上,这一点直接决定后面往哪个方向查。
见过有人还在用--zookeeper参数,那个写法在Kafka 2.0.0之后就被删了,2.0.0、3.0.0、4.0.0,全都只认--bootstrap-server。手上是Kafka 0.9.0.0、0.10.0.0这种年份很早的集群时,消费组的位移存在ZooKeeper里,得用zookeeper-shell.sh去/consumers路径下翻,命令完全不一样。近两年新部署的Kafka 4.0.0、4.1.0、4.2.0,已经不带ZooKeeper了,KRaft是唯一模式。
分区数不够时加消费者是白加
一个分区在同一时刻只能被组内的一个消费者实例消费,这条限制绕不过去。消费者实例数超过分区数,多出来的实例处于闲置状态,--describe输出的CONSUMER-ID那一列是横线。实测过一组3分区的topic开6个实例,其中3个从头到尾空转,LAG一点没降。
这种情况只有两条路:加分区,或者让单个实例消费得更快。加分区的命令:
bin/kafka-topics.sh --bootstrap-server 10.0.0.11:9092 --alter \
--topic order-event --partitions 12
加分区前要确认一件事:消息如果是按key发的,且业务依赖同一个key的消息顺序,加分区会改变key到分区的映射关系,原来落在同一分区的key会被打散。这种topic不能靠加分区解决,只能改单实例吞吐。
消费者被踢出组,LAG反弹
这一类是遇到最多的:LAG先降下来,过一阵突然又涨回去,日志里出现消费组rebalance。看消费者自己的日志,会有这么一句:
Consumer poll timeout has expired. This means that the time between subsequent calls to poll() was longer than the configured max.poll.interval.ms
max.poll.interval.ms默认300000,也就是5分钟。poll一批消息之后,业务处理花了超过5分钟还没再调用poll,消费者就被判定死亡、踢出组,剩下的实例重新分配分区。麻烦的是踢出去之前处理了一半的消息还没提交位移,接手的实例从上一次提交的位置重新拉,同一批消息被消费两遍,LAG跟着反弹。
处理办法按优先级排:把max.poll.records从默认的500往下调,让每批少一点;耗时逻辑丢给线程池,poll线程只管拉和提交;这两步都不行才去调大max.poll.interval.ms,代价是真出问题的消费者要更久才会被发现。另外session.timeout.ms默认45000、heartbeat.interval.ms默认3000,这两个一般不用动。
位移被重置或过期
两种表现完全相反。auto.offset.reset配成latest时,消费组第一次启动、或者位移记录已经不存在,就从分区末尾开始读,中间的整段直接跳过。这种不是堆积,是丢数据,LAG看着是0但业务侧就是少了单子。配成earliest时反过来,消费组一重启就从最早的位置重放,屏幕上突然冒出几百万的LAG。
中间的界线是offsets.retention.minutes,默认10080分钟,也就是7天。消费组停摆超过7天,__consumer_offsets里对应的位移记录被清理,再拉起来就按auto.offset.reset走。2025年接手过一套集群,Kafka 3.9.0、3.8.0,两个版本混着跑,其中一个消费组因为发布失败停了十来天,恢复后LAG直接几百万,根子就在这条配置上。
处理慢到底是慢在哪
上面几种排完,剩下的就是消费逻辑本身慢。最直接的办法是在poll的回调里打点,记下每批的处理条数和耗时,打十来分钟就能看出是均匀慢还是被个别大消息拖住。不想改代码的,看JMX指标records-lag-max,它在kafka.consumer:type=consumer-fetch-manager-metrics下面,能看出单个分区的最大堆积。
之前碰到过一个典型的:单批处理耗时看起来正常,但每批开头总有那么一两秒卡住,结果发现是批里带了两条大消息,反序列化加解压缩就吃掉两秒。这种把max.partition.fetch.bytes从默认的1048576调小没用,得在生产者侧限制单条大小,或者把大字段挪到对象存储、消息里只留引用。
来不及细查时怎么止损
堆积已经影响到线上,先决定是追数据还是跳过去。跳过去用--reset-offsets:
bin/kafka-consumer-groups.sh --bootstrap-server 10.0.0.11:9092 --group order-consumer \
--topic order-event --reset-offsets --to-latest --dry-run
先跑--dry-run看影响到的分区和目标位移,确认无误再把--dry-run换成--execute。消费组里还有活跃成员时会直接报错,提示只能对空组做重置,这时候得先把消费者实例停掉。
日常盯什么
别用定时任务去跑kafka-consumer-groups.sh再解析文本,消费组一多这个命令跑一次要好几秒,跑频繁了还顺带给broker加压。用JMX的records-lag-max,或者上Burrow,它专门做消费堆积监控,按topic和消费组出堆积趋势。
分诊顺序就这几步:看LAG分布,看消费者实例是不是比分区多,看有没有rebalance和poll超时,看位移有没有被重置,再去看消费逻辑本身。数据截至2026年10月。