Redis
目前最新版本为
Redis-6.2.6
,会以 CentOS7 下
Redis-6.2.4
版本进行讲解。
下载地址:
https://redis.io/download
安装运行
Redis
很简单,在
Linux
下执行上面的
4
条命令即可 ,同时前面的
课程已经有完整的视频讲解,请到网盘中下载观看,并自行安装。如安装过程出
错,请保证安装包完整无误
,
依赖包无误,并仔细阅读安装错误日志和检查操作
系统层面的用户、用户组、文件和目录是否存在,各种权限是否正确等!
同时
Redis
的官方文档相当丰富和齐全,
WEB
地址如下:
https://redis.io/documentation
Redis 队列与 Stream、Redis 6 多线程
详解
Redis 队列与 Stream
Redis5.0
最大的新特性就是多出了一个数据结构
Stream
,它是一个新的强大的
支持多播的可持久化的消息队列,作者声明
Redis Stream
地借鉴了
Kafka
的设计。
Stream
总述
Redis Stream
的结构如上图所示,每一个Stream都有一个消息链表,将所有加入 的消息都串起来,每个消息都有一个唯一的 ID
和对应的内容。消息是持久化的,Redis 重启后,内容还在。
每个 Stream
都有唯一的名称,它就是
Redis
的
key
,在我们首次使用
xadd
指 令追加消息时自动创建。
每个 Stream
都可以挂多个消费组,每个消费组会有个游标
last_delivered_id
在 Stream 数组之上往前移动,表示当前消费组已经消费到哪条消息了。每个消费 组都有一个 Stream
内唯一的名称,消费组不会自动创建,它需要单独的指令 xgroup create进行创建,需要指定从
Stream
的某个消息
ID
开始消费,这个ID 用来初始化
last_delivered_id
变量。
每个消费组 (Consumer Group)
的状态都是独立的,相互不受影响。也就是 说同一份 Stream
内部的消息会被每个消费组都消费到。
同一个消费组
(Consumer Group)
可以挂接多个消费者
(Consumer)
,这些消费 者之间是竞争关系,任意一个消费者读取了消息都会使游标last_delivered_id
往 前移动。每个消费者有一个组内唯一名称。
消费者 (Consumer)
内部会有个状态变量
pending_ids
,它记录了当前已经被 客户端读取,但是还没有 ack
的消息。如果客户端没有
ack
,这个变量里面的消 息 ID
会越来越多,一旦某个消息被
ack
,它就开始减少。这个
pending_ids
变 量在 Redis
官方被称之为
PEL
,也就是
Pending Entries List
,这是一个很核心的 数据结构,它用来确保客户端至少消费了消息一次,而不会在网络传输的中途丢 失了没处理。
消息 ID
的形式是
timestampInMillis-sequence
,例如
1527846880572-5
,它 表示当前的消息在毫米时间戳 1527846880572
时产生,并且是该毫秒内产生的第
5
条消息。消息
ID
可以由服器自动生成,也可以由客户端自己指定,但是形 式必须是整数-
整数,而且必须是后面加入的消息的
ID
要大于前面的消息
ID
。
消息内容就是键值对,形如 hash
结构的键值对,这没什么特别之处。
常用操作命令
生产端
xadd 追加消息
xdel 删除消息,这里的删除仅仅是设置了标志位,不会实际删除消息。
xrange 获取消息列表,会自动过滤已经删除的消息
xlen 消息长度
del 删除
Stream
xadd streamtest * name mark age 18
streamtest 表示当前这个队列的名字,也就是我们一般意义上
Redis
中的
key
, * 号表示服务器自动生成 ID
,后面顺序跟着“
name mark age 18
”,是我们存入 当前 streamtest
这个队列的消息,采用的也是
key/value
的存储形式
返回值 1626705954593-0
则是生成的消息
ID
,由两部分组成:时间戳
-
序号。 时间戳时毫秒级单位,是生成消息的 Redis
服务器时间,它是个
64
位整型。序 号是在这个毫秒时间点内的消息序号。它也是个 64
位整型。
为了保证消息是有序的,因此 Redis
生成的
ID
是单调递增有序的。由于
ID 中包含时间戳部分,为了避免服务器时间错误而带来的问题(例如服务器时间延 后了),Redis
的每个
Stream
类型数据都维护一个
latest_generated_id
属性,用 于记录最后一个消息的 ID
。若发现当前时间戳退后(小于
latest_generated_id
所 记录的),则采用时间戳不变而序号递增的方案来作为新消息 ID
(这也是序号 为什么使用 int64
的原因,保证有足够多的的序号),从而保证
ID
的单调递增性
质。
如果不是非常特别的需求,强烈建议使用 Redis
的方案生成消息
ID
,因为这 种时间戳+
序号的单调递增的
ID
方案,几乎可以满足全部的需求,但
ID
是支持 自定义的。
xrange streamtest - +
其中
-
表示最小值
, +
表示最大值
或者我们可以指定消息
ID
的列表:
xdel streamtest 1626706380924-0
xlen streamtest
del streamtest
删除整个
Stream
消费端
单消费者
虽然 Stream
中有消费者组的概念,但是可以在不定义消费组的情况下进行 Stream 消息的独立消费,当
Stream
没有新消息时,甚至可以阻塞等待。
Redis
设 计了一个单独的消费指令 xread
,可以将
Stream
当成普通的消息队列
(list)
来 使用。使用 xread
时,我们可以完全忽略消费组
(Consumer Group)
的存在,就 好比 Stream
就是一个普通的列表
(list)
。
xread count 1 streams stream2 0-0
“count 1
”表示从
Stream
读取
1
条消息,缺省当然是头部,“
streams
” 可以理解为 Redis
关键字,“
stream2
”指明了要读取的队列名称,“
0-0
”指从 头开始
xread count 2 streams stream2 1626710882927-0
也可以指定从 streams
的消息
Id
开始
(
不包括命令中的消息
id)
xread count 1 streams stream2 $
$代表从尾部读取,上面的意思就是从尾部读取最新的一条消息
,
此时默认不返回任何消息
所以最好以阻塞的方式读取尾部最新的一条消息,直到新的消息的到来
xread block 0 count 1 streams stream2 $
block
后面的数字代表阻塞时间,单位毫秒
此时我们新开一个客户端,往
stream2
中写入一条消息
可以看到阻塞解除了,返回了新的消息内容,而且还显示了一个等待时间,
这里我们等待了
127.87s
一般来说客户端如果想要使用
xread
进行顺序消费,一定要记住当前消费
到哪里了,也就是返回的消息
ID
。下次继续调用
xread
时,将上次返回的最后
一个消息
ID
作为参数传递进去,就可以继续消费后续的消息。
消费组
创建消费组
Stream 通过
xgroup create
指令创建消费组