|
11 | 11 | content: Redis消息队列,Redis Stream,Redis List,Redis Pub/Sub,消息队列,消费者组,ACK机制,XREADGROUP,XADD,XACK |
12 | 12 | --- |
13 | 13 |
|
14 | | -先说结论:**可以是可以,但不建议使用 Redis 来做消息队列。和专业的消息队列相比,还是有很多欠缺的地方。** |
| 14 | +先说结论:**可以是可以,但要看具体场景。和专业的消息队列(如 Kafka、RabbitMQ)相比,还是有一些欠缺的地方。** |
15 | 15 |
|
16 | 16 | 正式开始介绍之前,我们先来看看:**一个生产级 MQ 需要具备哪些核心能力?** |
17 | 17 |
|
@@ -71,36 +71,36 @@ Pub/Sub 涉及发布者(Publisher)和订阅者(Subscriber,也叫消费 |
71 | 71 | - 发布者通过 `PUBLISH` 投递消息给指定 Channel。 |
72 | 72 | - 订阅者通过`SUBSCRIBE`订阅它关心的 Channel。并且,订阅者可以订阅一个或者多个 Channel。 |
73 | 73 |
|
74 | | -也就是说,多个消费者可以订阅同一个Channel,生产者向这个Channel发布消息,所有订阅者都能收到。 |
| 74 | +也就是说,多个消费者可以订阅同一个 Channel,生产者向这个 Channel 发布消息,所有订阅者都能收到。 |
75 | 75 |
|
76 | 76 | 我们这里启动 3 个 Redis 客户端来简单演示一下: |
77 | 77 |
|
78 | 78 |  |
79 | 79 |
|
80 | 80 | Pub/Sub 既能单播又能广播,还支持 Channel 的简单正则匹配。 |
81 | 81 |
|
82 | | -Pub/Sub有一个致命的缺陷:**它发后即忘,完全没有持久化和可靠性保证**。 如果消息发布时,某个消费者不在线,或者网络抖动了一下,那这条消息对它来说就永远丢失了。此外,它也**没有 ACK 机制**,无法知道消费者是否成功处理,更别提**消息堆积**的问题了。所以,Pub/Sub 只适合做一些对可靠性要求极低的实时通知,绝对不能用于任何严肃的业务消息队列。 |
| 82 | +Pub/Sub 有一个致命的缺陷:**它发后即忘,完全没有持久化和可靠性保证**。 如果消息发布时,某个消费者不在线,或者网络抖动了一下,那这条消息对它来说就永远丢失了。此外,它也**没有 ACK 机制**,无法知道消费者是否成功处理,更别提**消息堆积**的问题了。所以,Pub/Sub 只适合做一些对可靠性要求极低的实时通知,绝对不能用于任何严肃的业务消息队列。 |
83 | 83 |
|
84 | 84 | ### 第三阶段:Redis 5.0 新增 Stream |
85 | 85 |
|
86 | 86 | Redis 5.0 新增了 `Stream` 数据结构。这是一个基于 Radix Tree(基数树)实现的有序消息日志,天然支持消费者组和 ACK 机制,可用于构建轻量级消息队列。 |
87 | 87 |
|
88 | 88 | **为什么要用 Radix Tree?** 很多人好奇,为什么不继续用 `List/LinkedList`? |
89 | 89 |
|
90 | | - 1. **内存极度压缩**:`Stream` 的消息 ID(如 `1625000000000-0`)是高度有序且前缀高度重合的。Radix Tree 是一种压缩前缀树,它会将具有相同前缀的节点合并。而 List/LinkedList |
91 | | - 每个元素都要完整的链表节点开销,并且无法利用 ID 的前缀重复特性来节省空间。 |
92 | | - 2. **高效检索**:在处理数百万级消息堆积时,Radix Tree 能保持极高的查询效率,这也是 `Stream` 能支持大数据量范围查询(`XRANGE`)的底层底气。相比之下,`List/LinkedList`只能从头尾操作,无法高效按 ID 范围查询,执行 `XRANGE` 需要遍历整个列表。 |
| 90 | +1. **内存极度压缩**:`Stream` 的消息 ID(如 `1625000000000-0`)是高度有序且前缀高度重合的。Radix Tree 是一种压缩前缀树,它会将具有相同前缀的节点合并。而 List/LinkedList |
| 91 | + 每个元素都要完整的链表节点开销,并且无法利用 ID 的前缀重复特性来节省空间。 |
| 92 | +2. **高效检索**:在处理数百万级消息堆积时,Radix Tree 能保持极高的查询效率,这也是 `Stream` 能支持大数据量范围查询(`XRANGE`)的底层底气。相比之下,`List/LinkedList`只能从头尾操作,无法高效按 ID 范围查询,执行 `XRANGE` 需要遍历整个列表。 |
93 | 93 |
|
94 | 94 | 它借鉴了 Kafka 等专业 MQ 的核心概念: |
95 | 95 |
|
96 | | - 1. **消费者组(Consumer Groups)**:实现消息在多个消费者间的负载均衡,支持故障自动转移。 |
97 | | - 2. **持久化**:可以通过 RDB 和 AOF 保证消息在 Redis 重启后不丢失(取决于 `appendfsync` 配置,`everysec` 模式下通常最多丢失 1 秒数据)。 |
98 | | - 3. **ACK 机制**:消费者处理完消息后,需要手动 `XACK` 确认,否则消息会保留在 `Pending List` 中。这保证了消息至少被成功消费一次。 |
99 | | - 4. **消息回溯与转移**:支持 `XRANGE` 按时间范围回溯消息,以及 `XCLAIM` 将挂起的消息转移到其他消费者处理。 |
| 96 | +1. **消费者组(Consumer Groups)**:实现消息在多个消费者间的负载均衡,支持故障自动转移。 |
| 97 | +2. **持久化**:可以通过 RDB 和 AOF 保证消息在 Redis 重启后不丢失(取决于 `appendfsync` 配置,`everysec` 模式下通常最多丢失 1 秒数据)。 |
| 98 | +3. **ACK 机制**:消费者处理完消息后,需要手动 `XACK` 确认,否则消息会保留在 `Pending List` 中。这保证了消息至少被成功消费一次。 |
| 99 | +4. **消息回溯与转移**:支持 `XRANGE` 按时间范围回溯消息,以及 `XCLAIM` 将挂起的消息转移到其他消费者处理。 |
100 | 100 |
|
101 | 101 | > 🌈 版本演进: |
102 | 102 | > |
103 | | -> - Redis 8.2:XACKDEL、XDELEX、XADD 和 XTRIM 命令提供了对流操作如何与多个消费者组交互的细粒度控制,简化了跨不同应用程序的消息处理协调。 |
| 103 | +> - Redis 8.2:`XACKDEL`、`XDELEX`、`XADD` 和 `XTRIM 命令提供了对流操作如何与多个消费者组交互的细粒度控制,简化了跨不同应用程序的消息处理协调。 |
104 | 104 | > - Redis 8.6:支持幂等消息处理(最多一次生产),防止在使用至少一次交付模式时出现重复条目。此功能可实现可靠的消息提交,并自动去重。 |
105 | 105 |
|
106 | 106 | `Stream` 的结构如下: |
@@ -199,11 +199,23 @@ sequenceDiagram |
199 | 199 |
|
200 | 200 | ### 总结 |
201 | 201 |
|
202 | | -**回到最初的问题:Redis 到底能不能做MQ?** |
| 202 | +**回到最初的问题:Redis 到底能不能做 MQ?** |
203 | 203 |
|
204 | 204 | - **如果业务简单、量小、追求极致性能**,且能容忍极小概率的数据丢失,使用 **Redis Stream** 是最优解,因为它省去了部署维护 MQ 的成本,可以复用现有的 Redis 组件(大部分需要用到 MQ 的项目,通常都会需要 Redis)。 |
205 | 205 | - **如果是金融级业务、海量数据、需要严格保证不丢消息**,必须选择 **Kafka、RabbitMQ** 等更成熟的 MQ。 |
206 | 206 |
|
| 207 | +更多 Redis 高频知识点和面试题总结,可以阅读笔者写的这几篇文章: |
| 208 | + |
| 209 | +- [Redis 常见面试题总结(上)](https://javaguide.cn/database/redis/redis-questions-01.html "Redis 常见面试题总结(上)")(Redis 基础、应用、数据类型、持久化机制、线程模型等) |
| 210 | +- [Redis 常见面试题总结(下)](https://javaguide.cn/database/redis/redis-questions-02.html "Redis 常见面试题总结(下)")(Redis 事务、性能优化、生产问题、集群、使用规范等) |
| 211 | +- [如何基于Redis实现延时任务](https://javaguide.cn/database/redis/redis-delayed-task.html "如何基于Redis实现延时任务") |
| 212 | +- [Redis 5 种基本数据类型详解](https://javaguide.cn/database/redis/redis-data-structures-01.html "Redis 5 种基本数据类型详解") |
| 213 | +- [Redis 3 种特殊数据类型详解](https://javaguide.cn/database/redis/redis-data-structures-02.html "Redis 3 种特殊数据类型详解") |
| 214 | +- [Redis为什么用跳表实现有序集合](https://javaguide.cn/database/redis/redis-skiplist.html "Redis为什么用跳表实现有序集合") |
| 215 | +- [Redis 持久化机制详解](https://javaguide.cn/database/redis/redis-persistence.html "Redis 持久化机制详解") |
| 216 | +- [Redis 内存碎片详解](https://javaguide.cn/database/redis/redis-memory-fragmentation.html "Redis 内存碎片详解") |
| 217 | +- [Redis 常见阻塞原因总结](https://javaguide.cn/database/redis/redis-common-blocking-problems-summary.html "Redis 常见阻塞原因总结") |
| 218 | + |
207 | 219 | 我的 [《SpringAI 智能面试平台+RAG 知识库》](https://javaguide.cn/zhuanlan/interview-guide.html)项目就是用的 Redis Stream 作为消息队列。在我的项目的场景下,它几乎是最合适的选择,完全够用了。 |
208 | 220 |
|
209 | 221 |  |
|
0 commit comments