基础
Stream结构相当于一个消息队列。消息是存储在磁盘上的,而且是链式结构。每个stream都有自己的唯名称,是redis的key。stream在集群情况下,也是异步复制的。
每个stream上可以有多个消费组,每个消费组都可以有自己的游标last_delivered_id
在Stream上往前移动,表示当前消费组消费到了那条消息。
消费组有Stream内的唯一的名称,使用xgroup_create
创建消费组并指定名称,创建的时候,需要指定某个消息的ID,该ID用于初始化last_delivered_id
变量。
同一个消费组的各个Consumer是竞争关系,只要有一个效消费了消息,那么就会移动last_derivered_id
。
消费者内部有一个状态变量pending_ids
,记录了当前已经被客户端读取但是还没有ack的消息。如果当前客户端没有ack,则其内部的消息ID越来越多,一旦某个消息被ack,则变量减少。该变量成为PEL (pending ertries list)
,该变量用来保证客户端至少消费了一次,并且不会在网络中途丢失了而没被处理。PEL
内部存储的是已经发出去的消息的ID。客户端在重连redis之后,可以再次收到PEL
中的ID列表。xreadgroup
的起始消息必须是任意有效的消息ID,一般参数设置为0-0,表示读取所有PEL
的消息以及last_delivered_id
之后的新消息。
如果消息一直没有ack,则会导致PEL
过大,消耗内存。
消息的ID是timeStampInMillis-sqquence,表示当前毫秒的消息序列;消息的内容是键值对,形如Hash结构的键值对。
基本操作方式
操作Stream:
- xadd:向Stream追加消息
- xdel:从Stream中删除消息,删除仅仅是设置标志位,不影响消息总长度。
- xrange:获取Stream中的消息列表,自动过滤已经删除的消息。
-
表示最小值,+
表示最大值。 - xlen:获取Stream的消息长度,所有在链表中存在的消息
- del:删除整个Stream中的所有消息。
xadd
命令,可以指定最长的长度,干掉旧的消息:
xadd myStream maxlen 1000 aaa bb ccc # 新增aaa bb ccc消息,同时链表不超过1000长度
独立消费:使用xread
指令,把Stream当作普通的链表使用,此时我们可以忽略消费组的存在。注意,使用xread
指令,一定要记得当前消费到的ID,下次调用xread
时,需要传入该ID作为参数。可以进行阻塞操作:
xread block 1000 count 1 streams myStream $
从myStream
队尾读取1个消息,不存在时阻塞1s
创建消费组
使用xgroup create
创建效费组,需要提供其实消息的ID来初始化last_delivered_id
变量。
比如:
xgroup create myStream cg1 0-0 # 从头部开始消费
xgroup create myStream cg2 $ # 从尾部开始消费
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uONW3ZH0-1574302168977)(en-resource://database/2763:1)]
消费消息
客户端使用xreadgroup
命令对组内消息进行消费,提供:消费组名称、消费者名称和启示消息ID,可以阻塞等待新的消息。消费者读取消息后,对一个的消息ID进入PEL中,消费者消费完毕后,需要发送XACK
,此时PEL中的结构会删除对应的消息。注意,消费完消息后,一定要执行XACK !!!!
比如:
xreadgroup GROUP cg1 c1 count 1 streams myStream
xreadgroup GROUP cg1 c1 block 0 count 1 streams myStream
Redis的Stream不支持分区,需要使用多个Stream才可以执行分区。