0
0
0

Redis 消息队列学习笔记

2026-10-28
文章摘要
|

一、Redis 消息队列的三种实现方式

Redis 可以通过三种方式实现消息队列:

  1. List:基于列表实现简单消息队列。

  2. Pub/Sub:基于发布订阅实现消息广播。

  3. Stream:支持消费者组、消息确认和故障恢复。

1. List 消息队列

List 可以通过 RPUSH 和 LPOP 实现先进先出的消息队列。

RPUSH queue msg1    # 生产者发送消息
LPOP queue          # 消费者获取消息
BLPOP queue 0       # 队列为空时阻塞等待

特点:

  • 支持多个生产者和多个消费者。

  • 支持阻塞读取。

  • 不具备原生 ACK 确认机制。

  • 消息弹出后就从 List 中移除,消费者宕机可能导致消息丢失。

注意: List 并不是只能单生产者、单消费者。多个消费者可以竞争消费同一个 List,但每条消息通常只会被其中一个消费者弹出。

2. Pub/Sub(发布订阅)

生产者通过 PUBLISH 发布消息,消费者通过 SUBSCRIBE 订阅频道。

SUBSCRIBE news          # 订阅频道
PUBLISH news "hello"    # 发布消息

特点:

  • 支持多个生产者和多个消费者。

  • 一条消息可以广播给多个在线订阅者。

  • 不持久化消息,也没有 ACK 确认机制。

  • 消费者不在线时,无法收到之前发送的消息。

  • 消费者处理速度过慢,可能因输出缓冲区达到限制而断开连接,导致消息丢失。

适用场景: 实时通知、消息广播等对消息可靠性要求不高的业务。

3. Stream 消息队列

Stream 是 Redis 5.0 引入的数据结构,提供了更完善的消息队列功能。

# 生产消息
XADD mq:orders * orderId 1001

# 读取消息
XREAD COUNT 1 BLOCK 2000 STREAMS mq:orders 0

特点:

  • 支持消息持久化(取决于 Redis 持久化配置)。

  • 支持阻塞读取。

  • 支持消费者组。

  • 支持 ACK 消息确认。

  • 支持 Pending List 记录未确认消息。

  • 支持历史消息回溯。

注意:Stream 并不保证绝对不丢消息,仍需要考虑 Redis 持久化、消息裁剪和消费者故障等问题。

二、三种消息队列对比

特性

List

Pub/Sub

Stream

消息持久化

可依赖 Redis 持久化

不支持

可依赖 Redis 持久化

多消费者

支持竞争消费

支持广播

支持消费者组

阻塞读取

支持

支持等待消息

支持

ACK 确认

无原生机制

不支持

支持

历史消息回溯

不支持已弹出消息

不支持

支持未删除的历史消息

故障恢复

需自行实现

不支持

支持 Pending 恢复

总结:

  • List:适合简单队列。

  • Pub/Sub:适合实时广播。

  • Stream:适合需要消息确认和故障恢复的队列。

三、Stream 消费者组

消费者组(Consumer Group)允许多个消费者共同处理同一个 Stream 中的消息。

主要有三个核心特点。

1. 消息分流

同一个消费者组内,多个消费者共同消费消息。

              Stream
                 |
          Consumer Group
                 |
        ┌────────┼────────┐
        ↓        ↓        ↓
     消费者A   消费者B   消费者C

同一条新消息通常分配给组内一个消费者处理,避免重复分发给所有消费者。

作用:提高消息处理能力,降低消息堆积风险。

不同消费者组之间有各自的消费进度,可以分别消费同一条消息。

2. 维护消费进度

Redis 会为消费者组记录最后投递的消息 ID。

当消费者读取新消息时,会根据消费组进度继续投递。

注意:记录的是消息投递进度,不是最后成功处理的消息。

如果消费者宕机,已经投递但未确认的消息不会自动作为新消息重新投递,需要通过 Pending List 恢复。

3. ACK 消息确认

消费者组读取消息后,消息会进入 Pending List。

生产者 XADD
     ↓
Stream 保存消息
     ↓
消费者 XREADGROUP
     ↓
消息进入 Pending List
     ↓
消费者处理业务
     ↓
处理成功 XACK
     ↓
从 Pending List 移除

重要:XACK 只会将消息从 Pending List 中移除,不会删除 Stream 中保存的原始消息。

四、Pending List 与消息恢复

Pending List(PEL)用于记录已经投递给消费者、但尚未 ACK 的消息。

1. 为什么需要 Pending List?

假设消费者 A 获取消息后突然宕机:

消费者 A 获取消息
       ↓
消息进入 Pending List
       ↓
消费者 A 宕机
       ↓
消息没有 XACK
       ↓
仍然保留 Pending 状态

如果没有恢复机制,这些消息可能长期得不到处理。

2. 常用命令

创建消费者组

XGROUP CREATE mq:orders g1 0 MKSTREAM

0 表示从 Stream 中最早的现存消息开始消费。

消费者读取新消息

XREADGROUP GROUP g1 c1 COUNT 10 BLOCK 2000 STREAMS mq:orders >

其中:

  • g1:消费者组。

  • c1:消费者名称。

  • >:读取尚未投递给该组的新消息。

查看未确认消息

XPENDING mq:orders g1

重新认领超时未确认的消息

XAUTOCLAIM mq:orders g1 c2 60000 0-0 COUNT 10

表示由消费者 c2 认领空闲至少 60 秒的 Pending 消息。

确认消费完成

XACK mq:orders g1 消息ID

业务执行成功后,使用 XACK 移除 Pending 记录。

五、Stream 如何处理消费者宕机?

完整流程:

消费者 A 获取消息
       ↓
消息进入 Pending List
       ↓
消费者 A 宕机
       ↓
未执行 XACK
       ↓
其他消费者通过 XPENDING 检查
       ↓
使用 XAUTOCLAIM 认领超时消息
       ↓
重新处理业务
       ↓
处理成功后 XACK

这样可以避免消费者宕机后消息一直无人处理。

注意:Redis 不会自动保证每条 Pending 消息都得到处理,需要应用程序实现恢复和重试逻辑。

六、Stream 的两个注意事项

1. 消息可能重复消费

例如:

  1. 消费者 A 已经完成数据库更新。

  2. 但还没执行 XACK 就宕机。

  3. 消费者 B 重新认领该消息。

  4. 相同业务可能再次执行。

因此,消费者必须考虑幂等性。

例如订单业务,可以根据订单 ID 防止重复创建订单。

2. 消息可能漏处理

使用 XREADGROUP ... > 只能读取新消息,不会自动读取其他消费者遗留的 Pending 消息。

因此,需要配合:

  • XPENDING:检查未确认消息。

  • XAUTOCLAIM:重新认领超时消息。

  • XACK:消费成功后确认。

同时,应避免错误设置消费起点或过早裁剪未处理消息。

七、总结

Redis 消息队列有三种实现方式:List、Pub/Sub 和 Stream。

List 可以通过 RPUSH、LPOP、BLPOP 实现简单队列,但没有原生消息确认机制。

Pub/Sub 通过 PUBLISH 和 SUBSCRIBE 实现消息广播,但不支持消息持久化和 ACK,消费者离线时容易丢失消息。

Stream 功能更加完善,支持消费者组、消息确认和历史消息读取。消费者组可以实现组内消息分流,并维护消费进度。

消息投递后进入 Pending List,处理成功通过 XACK 确认。如果消费者宕机,可以通过 XPENDING 检查、XAUTOCLAIM 认领未确认消息,实现故障恢复。

但 Stream 仍可能发生重复消费,因此业务需要做好幂等处理。

核心记忆:List 实现简单队列,Pub/Sub 实现广播,Stream 通过消费者组、Pending List 和 ACK 实现更可靠的消息消费。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

评论