任务队列积压严重时,先别急着扩容消费者。更稳妥的顺序是先判断“生产端突然放量、消费端变慢或停摆、还是有毒消息反复重试”,再确认积压任务能不能丢、能不能延迟、是否必须保序。队列实现不同,排查入口也不同:Redis List/Stream 看长度和消费组滞后,RabbitMQ 看 ready 与 unacked,Kafka 看 lag,数据库表队列看状态分布和锁等待。判断清楚之前,直接 purge 或批量删除容易把可恢复问题变成数据丢失。
面对任务队列积压,建议先把目标限定为“止住新增、恢复消费、再清理存量”,不要一上来清空队列。可先确认消费者是否存活、下游依赖是否超时、失败任务是否集中在同一类消息;随后隔离毒消息、暂停非核心生产、按下游承载逐步提高消费能力。验证时看积压量是否持续下降、失败率是否回落、下游是否被打满。清理存量前要确认任务可丢弃或可重放,purge、重置 offset、批量删除都属于高风险动作,需要保留现场和回退路径。
先确认当前场景和目标
问题成立的前提是:积压量在持续增长,或积压量稳定但消费延迟已超过业务可接受范围。需要先确认队列类型和部署形态、积压趋势是否按分钟级在涨、消费者进程和线程是否存活、是否存在大量 unacked 或重试任务、任务是否幂等以及能否延迟、业务允许的最长延迟、下游数据库或第三方接口是否已经异常。这些信息没确认前,不建议执行清理动作。
可以先做只读检查,不要改配置。下面命令按环境替换队列名和消费组名:
# Redis List / Stream 通用查看,注意大 key 不要全量 scan
redis-cli LLEN job:queue
redis-cli XLEN job:stream
redis-cli XINFO GROUPS job:stream
# RabbitMQ 查看 ready 与 unacked
rabbitmqctl list_queues name messages messages_ready messages_unacknowledged
# Kafka 查看消费组 lag
kafka-consumer-groups.sh --bootstrap-server broker:9092 --describe --group job-worker数据库表队列可以先按状态聚合,再抽几条最老任务看错误信息:
SELECT status, COUNT(*) AS cnt, MIN(created_at) AS oldest
FROM job_queue
GROUP BY status;
SELECT id, type, retry_count, last_error, created_at
FROM job_queue
WHERE status = 'pending'
ORDER BY created_at
LIMIT 20;这些结果只用于判断,不要据此直接删数据。如果消费组 lag 在涨但消费者进程还在,通常优先查消费线程卡住、下游超时、批量参数过大或单条消息处理过慢;如果 pending 集中在同一 type,优先怀疑毒消息或某类任务依赖故障。
按这个顺序处理
建议按“止血—定位—隔离—恢复—清理”的顺序推进,避免边扩容边积压。

- 先止住新增。暂停非核心生产者、降低投递频率或把新任务转入延迟队列,核心链路保留。判断条件是新增速率是否仍明显高于消费速率。
- 确认消费端是否真的在工作。看进程、线程池活跃数、最近成功消费时间、日志里是否有超时、连接池耗尽或锁等待,不要只看进程存在。
- 把积压分类。新任务、重试任务、死信任务分开统计。重试任务占比高时,先处理失败原因,不要盲目提高重试次数。
- 隔离毒消息。对反复失败的消息记录 id、错误栈和入队时间,转入死信表或暂停消费,避免占住消费者。下面只是消费端保护骨架,不是官方 SDK 接口:
def handle(job):
try:
process(job)
except RetryableError:
requeue_with_backoff(job) # 可重试:退避后重入
except Exception as e:
move_to_dead_letter(job, e) # 不可重试:隔离,保留原始报文
alert(job.type, job.id)按你们的队列客户端替换 requeue 和 move_to_dead_letter 的实现。隔离后观察该 type 的失败是否停止扩散。
- 再恢复消费能力。扩容消费者前先确认下游数据库、缓存、第三方接口能否承接;如果下游已接近瓶颈,扩消费者只会把故障转移。可以小步增加并发,观察错误率和响应时间。
- 最后清理存量。可丢弃任务按业务规则归档后删除;可重放任务先小批量回放验证;必须保序的队列不要并行清理。数据库队列删除要分批,避免长事务锁表:
-- 先选 id,再按主键分批删除,避免一次大事务
SELECT id FROM job_queue
WHERE status = 'failed' AND created_at < :cutoff
ORDER BY id
LIMIT 1000;
DELETE FROM job_queue WHERE id IN (:ids);Redis 的 DEL 大 key、RabbitMQ 的 purge、Kafka 的 offset 重置都属于不可逆或影响面大的动作,执行前要确认队列名、环境和备份。清理期间保留一条只读通道,方便抽样核对。
改完后怎么验证
验证不要只看队列长度变短了,要看积压是否持续下降、消费是否稳定。可执行动作包括:连续观察入队速率和出队速率,至少覆盖一个业务周期;抽样任务从入队到消费完成,检查结果写入、幂等键和重试记录是否一致;观察失败率和死信数量,失败任务应被隔离而不是反复重试;观察数据库连接数、慢查询、第三方接口超时是否随消费恢复而升高;检查 unacked 或处理中数量是否回落,避免消费者看起来在跑但消息一直不确认。
预期信号是积压斜率转负、消费延迟逐步回到业务可接受范围、错误率不高于处理前。如果扩容后积压不降反升,优先回退并发、检查下游瓶颈和毒消息,而不是继续加消费者。
容易忽略的边界
第一,队列语义。至少一次投递的队列在清理和重放时可能产生重复任务,消费端要有幂等键;顺序队列不能为了提速随意并行。第二,权限和影响面。排查账号建议只读,purge、删除、重置 offset 需要单独审批;RabbitMQ purge 会清空整个队列,不是只清一部分。第三,数据库队列的批量删除要控制批次和锁等待,避免清理动作把业务库拖慢。第四,Redis 大 key 删除可能阻塞,Kafka 重置 offset 会影响对应消费者组,需要确认范围。回退建议是:清理前先导出待删任务 id 和关键字段,保留原始消息或快照;如果验证发现任务被误删,先停止后续批次,用备份或重放通道恢复,不要继续扩大删除范围。对不能确认是否可丢的任务,可以先归档到冷表或对象存储,再延迟删除。