消息队列积压怎么排查?别一上来就加消费者 消息队列是后端系统里很常见的异步解耦工具。比如订单创建后异步发通知 文件上传后异步转码 用户操作后异步写日志 会议结束后异步生成总结 数据变更后异步同步缓存正常情况下生产者把消息写入队列消费者不断取出并处理整个链路比较平稳。但线上系统经常会遇到一个问题消息突然积压。监控里看到队列长度不断上涨ready messages: 10000 ready messages: 50000 ready messages: 200000很多人的第一反应是加消费者。但消息积压不一定是消费者数量不够。盲目扩容消费者有时反而会把数据库、Redis 或第三方接口打垮。排查 MQ 积压应该先搞清楚消息为什么消费不动。一、先确认积压发生在哪里消息队列链路通常可以拆成三段生产者 → 消息队列 → 消费者 → 下游服务积压可能发生在不同位置。常见情况包括生产速度突然变快 消费者处理速度变慢 消费者实例异常下线 下游数据库或接口变慢 消息反复失败重试 单条消息处理时间过长所以第一步不是改代码而是先看几个指标生产速率每秒写入多少消息 消费速率每秒处理多少消息 队列长度积压是否持续增长 失败率是否大量消费失败 重试次数是否出现重复重试 消费者在线数量是否少于预期如果生产速率没有变化但消费速率突然下降就要重点看消费者和下游依赖。如果生产速率突然暴涨就要看是否有活动、批量任务或异常循环生产消息。二、检查消费者是否正常运行有时候问题很简单消费者挂了。例如服务发布后消费者没有启动 消费者线程池耗尽 消费者连接 MQ 失败 配置写错导致监听队列不对 某个异常导致消费线程退出可以先检查消费者日志。正常消费者应该持续打印类似日志consume message success, messageIdxxx consume message failed, messageIdxxx如果一段时间内完全没有消费日志就说明消费者可能没有真正工作。在 Spring 项目中也要确认监听器配置是否正确RabbitListener(queues summary_task_queue) public void consume(String message) { // 处理消息 }队列名、交换机、路由键配置错误都会导致消息进入了一个队列但消费者监听的是另一个队列。三、单条消息处理是否太慢有些消费者看起来在工作但每条消息处理很久。例如一条消息要做这些事查询数据库 调用 AI 服务 生成文件 上传对象存储 更新任务状态 发送通知如果每条消息处理 5 秒一个消费者每分钟只能处理 12 条。当生产速度达到每分钟几百条时积压自然会出现。建议在消费者内部记录分阶段耗时long start System.currentTimeMillis(); try { handleMessage(message); } finally { log.info( consume_cost messageId{} cost{}ms, message.getId(), System.currentTimeMillis() - start ); }如果业务逻辑比较复杂还可以继续拆query_db_cost call_ai_cost upload_file_cost update_status_cost只有知道慢在哪里才知道该优化什么。四、下游依赖慢会拖垮消费者消费者本身可能没有问题真正变慢的是下游。常见下游包括MySQL Redis 对象存储 搜索引擎 第三方 API AI 模型服务例如同言翻译Transync AI这类实时翻译产品会议结束后可能会异步处理翻译记录、生成 AI 会议总结、统计任务使用明细、整理多语言内容。这些任务适合放入消息队列但如果下游的总结生成服务变慢消费者处理速度就会下降队列也会开始积压。这时盲目增加消费者可能会导致更多请求同时打到下游服务进一步放大故障。更合理的做法是先确认下游是否变慢 给下游调用设置超时 限制消费者并发 必要时降级非核心任务五、是否出现了重试风暴消息消费失败后通常会重试。适度重试是必要的但如果没有限制就可能形成重试风暴。例如一条消息处理失败 立即重新入队 再次被消费 再次失败 再次入队如果有大量消息都这样循环会导致消费者一直处理失败消息正常消息反而被堵住。建议重试要有上限第一次失败延迟 10 秒重试 第二次失败延迟 1 分钟重试 第三次失败延迟 5 分钟重试 超过上限进入死信队列伪代码if (message.getRetryCount() 3) { sendToDeadLetterQueue(message); return; } message.increaseRetryCount(); sendToDelayQueue(message);不要让失败消息无限回到主队列。死信队列中的消息可以后续人工排查或定时补偿。六、是否存在热点消息或顺序消费阻塞有些队列要求顺序消费。例如同一个用户、同一个订单、同一个任务的消息必须按顺序处理。顺序消费的缺点是如果前面一条消息卡住后面的消息也会被阻塞。例如task_10001 的第 1 条消息处理失败 task_10001 的第 2、3、4 条消息都不能继续处理这时要考虑是否真的需要全局顺序 是否只需要局部顺序 是否可以按 taskId 分区 是否可以把失败消息转入异常队列不要为了少量顺序要求把整个队列都变成单线程处理。七、能不能直接加消费者可以但要先确认瓶颈。如果瓶颈在消费者 CPU 计算增加消费者可能有效。如果瓶颈在数据库增加消费者可能让数据库更慢。如果瓶颈在第三方接口限流增加消费者只会产生更多失败。可以这样判断现象可能原因是否适合加消费者CPU 很高本地计算重可以考虑数据库慢查询多DB 瓶颈不建议盲目增加第三方接口超时外部依赖慢不建议消费者数量不足实例少可以考虑大量失败重试逻辑异常先处理失败原因扩容消费者前最好先压测单个消费者的处理能力再估算所需并发数。八、消息积压时的应急处理如果线上已经严重积压可以按优先级处理。第一步先止血暂停非核心生产任务 关闭异常入口 限制批量操作 降低重试频率第二步保护下游限制消费者并发 设置接口超时 熔断异常依赖 避免把数据库打满第三步分批恢复优先处理高优先级消息 低价值消息可以丢弃或延迟处理 死信队列单独排查 积压任务分批消费不要在系统最脆弱的时候一次性放开所有消费者。九、消息队列需要哪些监控建议至少监控这些指标队列长度 生产速率 消费速率 消费失败率 平均消费耗时 P95 / P99 消费耗时 重试消息数量 死信队列数量 消费者在线数量 下游接口耗时告警不要只看队列长度。有些队列短时间增长是正常的比如夜间批处理或活动高峰。更有价值的告警是队列长度持续增长 10 分钟 消费速率长期低于生产速率 失败率突然升高 死信队列数量持续增加这样才能减少误报。十、排查清单遇到 MQ 积压时可以按这个顺序排查1. 生产速率是否突然升高 2. 消费者实例是否正常在线 3. 消费速率是否下降 4. 单条消息平均耗时是否变长 5. 下游数据库、Redis、API 是否变慢 6. 是否有大量失败重试 7. 是否有消息进入死信队列 8. 是否存在顺序消费阻塞 9. 是否可以临时关闭非核心生产入口 10. 是否真的适合增加消费者总结消息队列积压不是一个单点问题而是整条异步链路的反馈。它可能来自生产端也可能来自消费者还可能来自数据库、外部服务或失败重试机制。排查时不要一上来就加消费者而是先确认消息是不是突然变多了 消费者是不是变慢了 下游是不是扛不住了 失败消息是不是一直在重试一套稳定的消息队列系统不只需要队列和消费者还需要消费耗时日志 失败重试上限 死信队列 下游限流 积压告警 应急降级方案只有这些机制都建立起来MQ 才是真正的削峰填谷工具而不是另一个隐藏故障点。