凌晨三点的消息堆积:当 MQ 变成“停车场“

凌晨三点,手机震了一下。

"杨工!订单支付成功的消息堆积了 80 万条!" 小刘的声音在电话里发抖,"用户付完钱收不到发货通知,客服电话被打爆了!"

我翻身下床,打开笔记本连上内网。监控大屏上,Kafka 消费组的 lag 曲线像火箭一样往上窜------80 万、90 万、100 万......

"别慌。" 我灌了一口冰水,"先分清是哪一侧的问题。"


第一幕:先看病根------生产端还是消费端?

小刘:"会不会是上游支付服务突然发了大量消息?"

我:"有可能,但概率小。先查生产端。"

我让他打开 Kafka 控制台,看最近一小时的 生产速率:

复制代码
生产速率:1200 msg/s → 正常

"生产端没问题。" 我松了口气,"那问题在消费端。消费端变慢只有两种可能:

  1. 单条消费耗时变长------逻辑变慢了,比如下游数据库卡了;
  2. 消费并发度不够------逻辑没变慢,但处理速度跟不上生产速度。"

小刘:"怎么判断是哪种?"

我:"看消费轨迹和日志。如果单条耗时从平时的 50ms 涨到 5 秒,说明消费逻辑卡住了;如果耗时正常但就是处理不过来,说明并发不够。"

小刘翻了几条消费日志:

复制代码
[2026-10-07 03:01:12] 消费消息耗时:4823ms
[2026-10-07 03:01:17] 消费消息耗时:5102ms
[2026-00:01:22] 消费消息耗时:4987ms

小刘:"平时 50ms,现在 5 秒!是消费逻辑卡住了!"

我:"抓消费端线程栈,看卡在哪。"

复制代码
jstack <consumer_pid> | grep -A 10 "WAITING\|BLOCKED"

输出里大量线程停在:

复制代码
java.net.SocketInputStream.socketRead0(Native Method)
  at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:xxx)

我 :"找到了。消费逻辑里查数据库,数据库慢了,线程全卡在 socketRead0 上。"


第二幕:应急处理------按优先级止血

小刘:"那怎么办?重启消费者?"

我:"重启没用,数据库还是慢。按优先级来:

1. 提高消费并发

先把消费者的线程数拉满。比如 Kafka 消费者从默认的 10 个线程调到 50 个。但注意------消费者实例数不能超过分区数,多出来的只会空转。"

小刘:"我们现在是 6 个分区、3 个实例,还能加。"

2. 扩容消费者实例

"再加 3 个实例,凑满 6 个分区。但记住,6 个就是上限了,再加也没用。"

3. 批量消费

"如果单条拉取网络开销大,改成批量拉取。一次拉 100 条,批量处理,减少网络往返。"

4. 临时转储

"如果还是追不上,写个临时消费者,把消息转存到本地文件或一个新 topic,先清空堆积,事后慢慢补处理。"

5. 丢弃非重要消息

"实在追不上、业务又能容忍的话,可以重置消费位点,跳过堆积的消息。但必须先跟业务方确认------丢了消息谁负责?"

小刘:"发货通知不能丢。先扩容 + 提并发,同时排查数据库为什么慢。"


第三幕:根因与修复

排查发现,凌晨两点有一个定时任务在做全量订单对账,锁住了订单表,导致消费端的查询全部阻塞。

修复方案:

  1. 临时:把对账任务暂停,数据库恢复;
  2. 消费端:线程数从 10 调到 50,实例从 3 扩到 6;
  3. 长期:给订单表加读写分离,对账走从库,不影响主库查询。

两小时后,堆积从 100 万降到 0,发货通知恢复正常。


第四幕:长期治理------踩坑后的加分项

第二天复盘会上,我补了几条长期治理建议:

治理方向 具体措施
优化单条消费耗时 DB 查询合并、非核心逻辑异步化、加缓存
幂等 消费逻辑必须幂等------堆积恢复过程中可能重复消费,比如同一笔订单发两次货就炸了
监控告警 消费位点延迟接入监控,lag 超过阈值就告警,别等客服电话打爆才发现
容量规划 按峰值流量规划消费者数量,大促前提前扩容

小刘:"幂等这个词我今天才真正理解。之前总觉得'我的逻辑不会重复',结果堆积一恢复,重复消费直接把库存扣成负数。"

我:"所以面试时答 MQ 堆积,最后一定要提幂等。能说出这个词,说明真踩过坑。"


尾声

凌晨五点,堆积清零,天也亮了。

小刘:"杨工,今天学到的东西,够我吹一年面试了。"

我:"记住,排查 MQ 堆积就像疏通停车场:先看是入口车太多(生产端突增)还是出口太慢(消费端变慢)→ 看单条耗时和并发度定位根因 → 按优先级止血(提并发 → 扩容 → 批量 → 转储 → 丢弃)→ 长期治理(优化耗时 + 幂等 + 监控)。套路熟了,凌晨就不慌了。"

他点点头,补了一句:"还有,以后写消费逻辑我一定先加幂等。"

我笑了:"这才是今晚最大的收获。"


监控大屏恢复绿色,消息流转如常。但我知道,下一场"停车场堵塞"迟早会来。

不过没关系,套路熟了,就不怕了。

相关推荐
谢亮_vipxieliang2 小时前
Go WaitGroup与Once——并发同步的基石
开发语言·后端·golang
ITOM运维行者2 小时前
PHP性能监控怎么做?从响应时间到慢函数的6个关键指标
前端·javascript·后端
成旭先生2 小时前
AI 文本审核 API:一段中文文本判风险等级、命中标签与处置建议
java·前端·人工智能·api接口·内容风控·文本审核·ugc审核
~~~4552 小时前
Java 数组详细笔记
java
Jinkxs2 小时前
Zookeeper - Java API 实现节点的创建与删除开发
java·zookeeper·java-zookeeper
liangshanbo12153 小时前
前端面试题:AI 对话中超长消息导致内存溢出,怎么解决?
java·开发语言·前端
RobinDevNotes3 小时前
亲手量化大模型,Mac实测和NVIDIA指南
人工智能·后端
极客先躯3 小时前
高级java每日一道面试题-2026年01月20日-实战篇[Docker]-如何实现镜像的跨区域复制?
java·运维·docker·容器·架构图
for_ever_love__3 小时前
Redis 持久化讲透:RDB、AOF 与混合持久化怎么选
java·数据库·redis·持久化·aof·区别·rdb