1. 核心概念
Redis Stream 是 Redis 5.0 引入的持久化消息队列数据结构,具有以下特性:
- 有序消息存储 :基于时间戳的全局唯一消息ID(格式:
<毫秒时间>-<序列号>
) - 消费者组模式:支持多消费者协同处理消息
- 数据持久化:依赖 Redis AOF/RDB 机制
- 内存高效:底层使用紧凑的基数树(Radix Tree)存储
2. 核心命令语法
2.1 添加流信息
bash
# 基本写入(自动生成ID)
XADD key [NOMKSTREAM] [MAXLEN | MINID [= | ~] threshold [LIMIT count]] * | id field value [field value ...]
参数解析:
key
: Stream 的键名。NOMKSTREAM
**: 如果 Stream 不存在,不自动创建(默认会自动创建)。MAXLEN
*: 限制 Stream 的最大长度(删除旧消息)。MAXLEN ~ threshold
: 近似裁剪(性能优化,允许略微超过阈值)。MAXLEN = threshold
: 精确裁剪(严格限制长度)。
MINID
: 删除 ID 小于threshold
的消息(类似按时间裁剪)。\*|ID
:*
: 自动生成消息 ID(格式为<timestamp>-<sequence>
,如1631234567890-0
)。- 手动指定 ID(需大于已有最大 ID)。
field value
: 消息的键值对(支持多个字段)。
示例:
shell
# 创建my_stream流 自动生成消息ID
xadd my_stream * name zhangsan age 25
> xadd my_stream * name zhangsan age 25
"1741586247360-0"
>
2.2 获取流信息
shell
# 从某一个消息ID开始等待消费消息
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]
参数解析
COUNT
:返回的消息数量BLOCK
:阻塞的时长(ms)。如果为0表示一直阻塞,直到有消息进来。默认不阻塞STREAMS
: 关键字(不可省略)key
: 队列名称id
:消息的ID。等待消费的消息从此ID往后,不包含此ID
示例:
shell
# 从流ID【1741586247360-0】开始获取流信息
xread streams my_stream 1741586247360-0
# 再次增加流信息
> xadd my_stream * name lisi age 18
"1741587049777-0"
# 从流ID【1741586247360-0】开始获取流信息
> xread streams my_stream 1741586247360-0
1) 1) "my_stream"
2) 1) 1) "1741587049777-0"
2) 1) "name"
2) "lisi"
3) "age"
4) "18"
2.3 流信息管理
shell
# 查看消息范围消息ID由小到大
XRANGE my_stream - + COUNT 3
# 查看消息范围消息ID由大到小
XREVRANGE my_stream + - COUNT 3
# 删除特定消息
XDEL mystream 1630000000000-0
-
:表示最小的流ID+
:表示最大的流ID
2.4 作为消息队列的使用
以上分别说明了流的创建、流的等待以及流的管理。很多人把上述的操作看做为消息的操作,严格意义上是不对的。如果作为消息的话2.3
里面对于流的获取就是消息的消费。
我们都知道,消息消费之后是不能被重复获取的。但是上面的操作是可以一直操作的。
可不可以作为消息队列使用呢,当然可以。官方提供了丰富的API。
2.4.1 创建消费者组
shell
# 创建消费者组
XGROUP CREATE key group id | $ [MKSTREAM] [ENTRIESREAD entries-read]
参数解析
key
: 流名称group
:消费者组的名称id
:初始消费的 ID(0
从头开始,$
从最新消息开始)。MKSTREAM
:如果 Stream 不存在,自动创建。ENTRIESREAD
: 高级用法,指定消费者组的初始读取位置(Redis 7.0+)。
案例:
shell
# 创建group_01消费者组,并初始化从头开始
xgroup create my_stream group_01 0
> xgroup create my_stream group_01 0
"OK"
2.4.2 消费者组内读取消息
shell
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] id [id ...]
参数解析:
GROUP
: 关键词group
: 消费者组名称consumer
:消费者名称NOACK
:不自动确认消息(需手动调用XACK
),默认自动确认消息,确认后消息被删除,但是流数据还会存在id
:>
: 读取未分配给其他消费者的消息。0
: 读取已分配给当前消费者但未确认的消息(用于重新处理)。
案例:
shell
# 创建消费者【my_consumer】消费消息。这时候消息没有被确认。
xreadgroup group group_01 my_consumer streams my_stream >
> xreadgroup group group_01 my_consumer streams my_stream >
1) 1) "my_stream"
2) 1) 1) "1741586247360-0"
2) 1) "name"
2) "zhangsan"
3) "age"
4) "25"
2) 1) "1741587049777-0"
2) 1) "name"
2) "lisi"
3) "age"
4) "18"
2.4.3 消息的确认
shell
XACK key group id [id ...]
参数解析:
group
:消费者组明id
: 消息的ID
案例:
shell
# 确认消费组group_01的消息1741587049777-0, 确认后group_01中对应的消息会被清除。但是流中的信息还在。
xack my_stream group_01 1741587049777-0
> xack my_stream group_01 1741587049777-0
(integer) 1
如果Stream中对用的消息被删除,消费者组还会正常消费么? 当然是可以的
3、与消息队列对比
维度 | Redis Stream | Kafka | RabbitMQ |
---|---|---|---|
消息持久化 | 内存存储+可选持久化 | 磁盘持久化 | 内存/磁盘 |
吞吐量 | 10万+/秒 | 百万级/秒 | 5万+/秒 |
延迟 | 亚毫秒级 | 毫秒级 | 微秒级 |
消费模式 | 发布订阅+消费者组 | 消费者组 | Exchange+队列 |
消息回溯 | 支持 | 支持 | 不支持 |
集群支持 | Redis Cluster | 原生分区 | 镜像队列 |
适用场景 | 实时处理、轻量级队列 | 大数据管道、日志收集 | 复杂路由、企业级MQ |
选型建议:
- 选择 Redis Stream:需要低延迟、简单部署、与Redis生态集成
- 选择 Kafka:处理海量数据、需要严格顺序保证
- 选择 RabbitMQ:需要复杂路由规则、死信队列等高级特性
4、监控指标
指标 | 检查命令 | 告警阈值 |
---|---|---|
消息积压量 | XPENDING mystream group |
> 1,000 |
消费者延迟 | XINFO CONSUMERS |
> 60秒 |
内存使用量 | MEMORY USAGE mystream |
> 80%最大内存 |
处理失败率 | 监控XCLAIM/XDEL次数 | > 5%/分钟 |
5、最佳实践
- 控制单个Stream的消息量(建议不超过1千万条)
- 合理设置
MAXLEN
防止内存溢出 - 消费者处理需实现幂等性
- 重要业务建议开启AOF持久化
- 定期监控Pending消息数量
适用场景:
- 实时消息处理(如聊天消息)
- 事件溯源系统
- 分布式任务队列
- 物联网数据管道
不适用的场景:
- 需要长期存储的海量数据(超过内存容量)
- 需要复杂消息路由规则的场景
- 严格的事务性消息处理