10.redis流(Stream)
Redis Stream是Redis5.0版本新增加的数据结构。主要用于消息队列(MQ,Message Queue)。
Redis本身就是一个Redis发布订阅(pub/sub),来实现消息队列的功能,但它有个缺点,就是消息无法持久化,如果出现网络断开、Redis宕机等,消息就会被丢弃。
而 Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端,访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。
redis常见数据类型操作命令
官网英文: https://redis.io/commands/
中文:http://www.redis.cn/commands.html
是什么
Redis5.0 之前的痛点,Redis消息队列的2种方案:
(1)List实现消息队列,List实现方式,其实就是点对点的模式

(2)Pub/Sub

img
Redis5.0版本新增了一个更强大的数据结构---Stream
一句话:Stream流就是Redis版的MQ消息中间件+阻塞队列
能干嘛
实现消息队列,它支持消息的持久化、支持自动生成全局唯一ID、支持ack确认消息的模式、支持消费组模式等,让消息队列更加的稳定和可靠
底层结构和原理说明

img
一个消息链表,将所有加入的消息都串起来,每个消息都有一个唯一的ID和对应的内容

img
队列相关指令

img
1.XADD
添加消息到队列末尾,消息ID必须要比上一个ID大,默认用星号表示自动生成ID;* 用于XADD命令中,让系统自动生成ID;
XADD用于向Stream队列中添加消息,如果指定的Stream队列不存在,则该命令执行时会新建一个Stream队列

img
生成的消息ID,有两部分组成,毫秒时间戳-该毫秒内产生的第一条消息
* 表示服务器自动生成MessageID(类似MySQL里面主键auto_increment),后面顺序跟着一堆业务key/value
标准语法:
# 自动生成ID(推荐)
XADD 队列名 MAXLEN ~ 最大长度 * 键1 值1 键2 值2 ...
# 手动指定ID(不推荐,必须严格遵守规则)
XADD 队列名 ID 键1 值1
关键参数
-
*:最常用,让 Redis 自动生成唯一消息 ID
-
MAXLEN ~ n:可选,限制队列最大长度,自动淘汰旧消息(节约内存)
-
消息必须是 成对的 key-value,不能为空
消息 ID 规则
- ID 格式
固定格式:时间戳-序列号
-
时间戳:Redis 服务器本地毫秒数
-
序列号:同一毫秒内的自增序号,从 0 开始递增
示例:
1745000000000-0 → 第 1 条消息
1745000000000-1 → 同一毫秒的第 2 条消息
1745000000001-0 → 下一毫秒的第 1 条消息
- 强制约束(Redis 底层保证)
-
全局单调递增:后一条 ID 一定 > 前一条 ID(Stream 核心特性)
-
时钟回拨自动修复:服务器时间倒退时,Redis 不会使用更小的时间戳,沿用上一个时间戳,只递增序列号
-
手动 ID 必须合法:格式必须是 数字-数字,且必须比上一条 ID 大
实操示例
示例 1:自动生成 ID(生产环境标准用法)
# 向 mystream 队列添加消息,自动生成ID
XADD mystream * name 张三 age 20 city 北京
# 再添加一条
XADD mystream * name 李四 age 25 city 上海
返回结果(自动生成的 ID):
1745012345678-0
1745012345679-0
示例 2:限制队列长度(防止内存爆炸)
# 队列最多保留 1000 条消息,满了自动删除旧消息
XADD mystream MAXLEN ~ 1000 * content "订单支付成功"
示例 3:手动指定 ID(仅测试用)
# 手动ID必须大于上一条
XADD mystream 1745012345680-0 content test
2.XRANGE key start end COUNT count
用于获取消息列表(可以指定范围),忽略删除的消息
start 表示开始值,-代表最小值
end 表示结束值,+代表最大值
count 表示最多获取多少个值

img
3.XREVRANGE key end start COUNT count
根据ID降序输出

img
4.XDEL key id id ...

img
5.XLEN key

img
6.XTRIM key MAXLEN|MINID
用于对Stream的长度进行截取,如超长会进行截取
MAXLEN 允许的最大长度,对流进行修剪限制长度
MINID 允许的最小id,从某个id值开始比该id值小的将会被抛弃

img
7.XREAD COUNT count BLOCK milliseconds STREAMS key key ... id id ...
可以读取多个key
用于获取消息(阻塞/非阻塞)
只会返回大于指定ID的消息,COUNT最多读取多少条消息;BLOCK是否以阻塞的方式读取消息,默认不阻塞,如果milliseconds设置为0,表示永远阻塞
非阻塞
-
$表特殊ID,表示以当前Stream已经存储的最大的ID作为最后一个ID,当前Stream中不存在大于当前最大ID的消息,因此此时返回nil
-
0-0代表从最小的ID开始获取Stream中的消息,当不指定count,将会返回Stream中的所有消息,注意也可以使用0 (00/000也都是可以的)

阻塞

img
Stream的基础方法,使用XADD存入消息,和XREAD循环阻塞读取消息的方式,可以实现简易版的消息队列

img
消费组相关指令

img
XINFO GROUPS 打印消费组的详细信息
XINFO STREAM 打印stream的详细信息
1.XGROUP CREATE key group id|$
用于创建消费组
xgroup create mystream group $
xgroup create mystream groupB 0
$表示从Stream尾部开始消费
0表示从Stream头部开始消费
创建消费组的时候必须指定ID,ID为0表示从头开始消费,为$表示只消费新消息

''
2.XREADGROUP GROUP group COUNT count BLOCK milliseconds STREAMS key id
">",表示从第一条尚未被消费的消息开始读取

img
消费组groupA内的消费者consumer1从mystream消息队列中读取所有消息
但是,不同消费组的消费者可以消费同一条消息

img
消费组的目的?
让组内的多个消费者共同分担读取消息,所以,我们通常会让每个消费者读取部分消息,从而实现消息读取负载在多个消费者间是均衡分部的

img
重点问题
基于 Stream 实现的消息队列,如何保证消费者在发生故障或宕机再次重启后,仍然可以读取未处理完的消息?
Streams 会自动使用内部队列(也称为 PENDING List)留存消费组里每个消费者读取的消息保底措施,直到消费者使用 XACK命令通知 Streams"消息已经处理完成"。消费确认增加了消息的可靠性,一般在业务处理完成之后,需要执行 XACK 命令确认消息已经被消费完成

img
3.XPENDING
查询每个消费组内所有消费组已读取、但尚未确认的消息

img
查看某个消费组具体读取了那些数据

img
4.XACK key group id id...
向消息队列确认消息处理已完成

img
5.XINFO 用于打印Stream\Consumer\Group的详细信息

img
四个特殊符号
| - + | 最小和最大可能出现的Id |
|---|---|
| $ | $表示只消费新的消息,当前流中最大的Id,可用于将要到来的信息 |
| > | 用于XREADGROUP命令,表示迄今还没有发送给组中使用者的信息,会更新消费者组的最后Id |
| * | 用于XADD命令,让系统自动生成Id |