大量消息在 MQ 里长时间积压,该如何解决?

Kafka 的 consumer lag 监控突然开始涨,上游消息一直在打进来,消费者追不上。积压的原因只有两种:消费速度跟不上生产速度 ,或者消费者根本没在消费。方向不同,处理方式完全不一样,搞反了越修越乱。

先判断消费者是活的还是卡死的

看消费者的 lag 曲线。lag 在涨但消费者还有 offset 提交,是消费速度不够。lag 在涨但 offset 长时间没动,消费者要么挂了,要么卡在某条消息上出不来。

Kafka 看 consumer group 状态:

bash 复制代码
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --describe --group your-group-name

LAGCONSUMER-ID 两列。CONSUMER-ID 都是空的,说明消费者全掉线了。有 CONSUMER-ID 但 LAG 持续增长,是速度问题。

消费者卡死的常见原因:消息反序列化失败一直重试、下游服务超时导致消费逻辑阻塞、消费者线程池打满。这种情况不是消费速度问题,先把卡死的消息处理掉,别急着扩容。一条格式异常的消息能把整个 consumer 卡住几个小时。

消费速度不够:临时扩分区、加消费者

Kafka 的消费并行度上限是分区数,一个分区同一时刻只能被一个消费者消费。topic 只有 4 个分区,启 8 个消费者实例也是浪费,有 4 个在闲置。

积压严重时的临时手段:

bash 复制代码
kafka-topics.sh --bootstrap-server localhost:9092 \
  --alter --topic your-topic --partitions 16

分区只能增不能减,增加后消费者组自动触发 rebalance,新分区分配到新消费者实例。分区数加了,同时拉起对应数量的消费者实例,并行度才真正提升。

注意这是应急操作,不是正常流程。分区数永久增加,消息的顺序性会被打乱(同一个 key 原来保证在同一分区,分区数变了 key 的 hash 落点变了)。如果业务依赖消费顺序,扩分区之前先确认影响范围。

消费逻辑本身太慢:批量消费、异步化

消费者一条一条拉,每条同步调用一次数据库。单条消费耗时 20ms,一个消费者线程一秒最多 50 条,分区再多也就这个吞吐量。

批量拉取:

java 复制代码
@KafkaListener(topics = "order-topic", containerFactory = "batchFactory")
public void consume(List<ConsumerRecord<String, String>> records) {
    List<Order> orders = records.stream()
        .map(r -> JSON.parseObject(r.value(), Order.class))
        .collect(Collectors.toList());
    orderMapper.batchInsert(orders);
}

批量 insert 比逐条 insert 快一个数量级,数据库连接开销从 N 次降到 1 次。max.poll.records 控制每次拉取的最大条数,根据单条消息处理耗时调整,不要设太大,否则处理时间超过 max.poll.interval.ms 会触发 rebalance。

消费逻辑里有外部调用(HTTP 接口、第三方 API)且不要求强一致,可以异步化:先把消息写本地数据库或者内存队列,立即提交 offset,另起线程批量处理。消费者不再被外部调用阻塞,速度能提升好几倍。代价是消费者重启可能丢失内存里还没处理的消息,本地数据库方案相对可靠。

积压太深:临时跳过或转移

积压量已经很大了,按正常消费速度需要好几个小时才能消化。如果这些是历史数据,业务上可以接受丢弃,直接重置 offset 到最新位置:

bash 复制代码
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --group your-group-name --topic your-topic \
  --reset-offsets --to-latest --execute

业务上不能丢的话,把积压的消息导到一个临时 topic,用专门的消费者集群在业务低峰期慢慢消化,主 topic 的消费者先处理新消息,不让积压继续扩大:

写一个转发消费者就行:从原 topic 消费,不做任何业务处理,直接 produce 到临时 topic。跑起来之后原 topic 的 lag 会快速下降,给正常消费者喘口气。

治标之后治本

积压消化完,还得找到为什么会积压。

最常见的是上游流量突增没有限流。促销活动订单量翻了好几倍,消费者没有弹性扩容机制,消费能力是固定的,生产端突然加速就积压了。消费者接入 Kubernetes HPA 按 lag 指标自动扩缩容可以缓解,但前提是消费逻辑本身能水平扩展。

还有一种更隐蔽的:还有一种更隐蔽的:消费逻辑里有慢查询,平时没问题,量大了之后数据库压力上来,SQL 执行时间翻了几十倍,消费速度直接掉一个数量级。这种不看 slow query 日志根本发现不了,以为是 Kafka 的问题,其实瓶颈在数据库。

积压告警本身也得提前配。lag 超过一定阈值就该响,等到积压量已经很大了再处理,扩分区、加消费者、转移消息,每一步都有风险。lag 还小的时候处理,可能加两个消费者实例就追上了。

相关推荐
qq_570398571 小时前
Three.js基础使用-案例
开发语言·javascript·ecmascript
Suxing91 小时前
C语言基础分享:C语言文件操作:从“写日记”到“拍电影”的存盘指南
c语言·开发语言
右耳朵猫AI1 小时前
Node.js周刊2026W36 | NestJS 12发布、Remix 3 RC、pnpm 12 Rust重写、Node.js 26.8.0
开发语言·rust·node.js
统计学小王子1 小时前
数学建模国赛倒计时 2 天 ——《异常值的处理(R语言)》
开发语言·数学建模·r语言
z落落4 小时前
C# WinForm Socket 转串口设备服务器+Modbus CRC16校验+Socket 客户端
服务器·开发语言·c#
小灰灰搞电子10 小时前
Rust+Slint 实现动态消息提示框源码分享
开发语言·后端·rust
传奇开心果编程12 小时前
【Rust入门知识点学与练】第21课:Trait 进阶 Advanced Traits
开发语言·学习·rust
新时代牛马12 小时前
字符设备驱动完整篇:从 cdev_add、file_operations 到chrdev_open 与排障
开发语言·python
swordbob12 小时前
ReentrantLock 与 AQS 完整学习手册
java·开发语言