因PUB/SUB不持久、无确认、不支持重试,而List可持久化、可控消费节奏,配合BRPOP阻塞等待避免空轮询,适合中小业务;虽无内置ACK,但可通过msg_id、processing哈希、LREM或重推等手段保障可靠性。

为什么不用PUB/SUB而选List做消息队列
因为PUB/SUB是“发完即焚”:订阅断开就丢消息,不持久、无确认、不支持重试。List才是真队列——消息存在Redis里,消费者宕机也不丢,还能控制消费节奏。
核心逻辑就是:LPUSH进队列,RPOP取任务,但直接RPOP会轮询空转耗CPU,必须搭配BRPOP阻塞等待。
-
BRPOP比RPOP多一个超时参数,没消息时挂起连接,不占CPU - 生产者用
LPUSH,保证新消息在队列头(FIFO需配合RPUSH+LPOP) - 单个List只能被一个消费者安全消费;多消费者需靠业务层加锁或用Stream
如何避免消息重复消费和丢失
Redis List本身不提供ACK机制,重复消费和丢失全靠你补逻辑。最简可行方案是“先处理再删”,但得防崩溃导致消息卡住。
推荐两步走:BRPOP取出消息 → 处理成功 → LREM删掉它。但LREM要小心匹配值,别误删其他同内容消息。
- 给每条消息加唯一
msg_id字段,存入List时用JSON字符串:{"id":"abc123","data":"..."} - 消费前先
HSET记录processing:abc123,超时设为30秒,防止死锁 - 处理失败时主动
LPUSH回队首(或另存到retry:queue),别直接丢弃
BRPOP阻塞超时设多少才合理
设太短(如100ms)等于假阻塞,频繁唤醒浪费连接;设太长(如30s)会导致新消息延迟感知。实际看业务容忍度。
Web后台任务通常设BRPOP queue_name 5(5秒),既避免空轮询,又能在突发流量下快速响应新消息。
- 高吞吐场景(如日志收集)可压到1~2秒,靠横向扩消费者扛住
- 低频定时任务(如每小时报表)设30~60秒也无妨
- 注意客户端连接数限制:每个
BRPOP占一个连接,别让空闲连接堆积
和Redis Stream比,List队列还值得用吗
值得。Stream功能强但复杂:要管GROUP、ACK、XPENDING,还要处理XCLAIM抢断。List简单粗暴,适合中小业务或过渡期。
但List有硬伤:不支持消息广播、无法按ID范围拉取、没有内置消费者组。如果已经用上Redis 5.0+且需要可靠投递,该切Stream了。
- 现有List队列别急着重构,加一层封装(比如封装
consume()函数自动重试+去重)更划算 - 新项目若QPS
- 别忽略监控:用
LLEN queue_name查积压,配合INFO clients看阻塞连接数
真正麻烦的不是命令怎么写,而是怎么让消息在Redis里待着不丢、在代码里跑完不漏、在监控里看得见——这些都得自己缝。

















