Kafka 消费端出现消息堆积和重复消费时,你会如何从分区、消费组、提交 offset 和业务幂等排查?
免费版我会把这个问题拆成两部分: 为什么消费速度跟不上导致堆积 ,以及 为什么同一条消息会被业务重复处理 。排查时一般从分区、消费组、Offset 提交和业务幂等四个层面入手。
1. 先确认堆积发生在哪个分区、哪个消费组
先看监控指标,重点关注:
Consumer Group 的总 Lag,以及各个分区的 Lag; 当前分区数、消费者实例数和实际分配关系; 消费速率和生产速率是否匹配; 是否存在某一个分区 Lag 特别高。
如果只有少数分区堆积,通常要重点看分区数据倾斜、某些 Key 集中到一个分区,或者这个分区对应的消费者处理了慢消息。Kafka 一个分区同一时刻只能被消费组内的一个消费者消费,所以如果分区数少于消费者数,多出来的消费者实际上是空闲的,单纯扩容消费者没有效果。
如果所有分区都在堆积,可能是整体消费能力不足,比如业务处理慢、数据库或下游服务变慢、线程池满、频繁 GC,或者消费者配置限制了拉取和处理能力。
2. 检查消费组和分区分配
确认消费者是否都正常加入同一个 Consumer Group,以及是否频繁发生 Rebalance。可以看消费者日志和 Kafka 监控,例如是否有:
partition revoked、partition assigned; 心跳超时、Session timeout; max.poll.interval.ms 超时; 消费者实例频繁重启或网络抖动。
如果业务处理是在 poll() 之后同步执行,处理时间超过了 max.poll.interval.ms,Kafka 会认为消费者失活并触发 Rebalance。原消费者处理完后再提交 Offset,新的消费者可能会从旧 Offset 重新拉取,这就容易造成重复消费,同时也会影响整体吞吐。
这类问题可以从几个方向处理:
降低单次 max.poll.records,避免一次拉取太多消息导致处理时间过长; 合理增大 max.poll.interval.ms,但不能把它当成解决业务慢的唯一手段; 优化单条消息处理耗时; 将拉取和业务处理解耦,通过内部线程池处理,但要控制并发、背压和提交时机; 尽量避免频繁发布、重启消费者实例。
同时要注意,消费者数量不能无限扩张,实际并发上限受分区数限制。要提升消费并行度,通常需要增加分区,但增加分区会影响顺序性、资源消耗和后续扩容成本,需要结合业务评估。
3. 排查 Offset 提交策略
Kafka 的消费进度本质上是提交到 Broker 的 Offset。要确认以下配置和代码行为:
是否开启了自动提交 enable.auto.commit; 自动提交的时间间隔是否合理; 是否使用了手动提交; 是 commitSync 还是 commitAsync; 提交的是当前消息 Offset,还是正确提交了下一条待消费 Offset; 业务处理失败时是否仍然提交了 Offset。
