Redis数据类型 - 流(Stream)

一、流(Stream)简介

流是Redis5.0版本中新增加的数据结构,是一个包含零个或任意多个流元素的有序队列,队列中的每个元素都包含一个ID和任意多个键值对,这些元素会根据ID的大小在流中有序的进行排列。如一个名为visits的流,其中每个元素包含一个ID和三个键值对:
在这里插入图片描述
通过将元素ID与时间进行关联,并强制要求新元素的ID必须大于旧元素的ID,Redis从逻辑上将流变成了一种只执行追加操作(append only)的数据结构,这种特性对于使用流实现消息队列和事件系统来说是非常重要的:可以确信,新的消息和事件只会出现在已有消息和事件之后,就像现实世界里新事件总是发生在已有事件之后一样,一切都是有序进行的。

二、流常用命令

  • 添加新的元素到流的末尾

key通过XADD命令,将一个带有指定ID以及包含指定键值对的元素添加到流的末尾;
语法格式:XADD key id field1 value1 field2 value2 …,如

# 向visits流中添加ID为1002的元素,且包含三个键值对
XADD visits 11000000000000-123456 name 'peter' location '/book/10086' duration 150

如果给定的流不存在,那么Redis会先创建一个空白的流,然后将给定的元素追加到流中。

流中的每个元素可以包含一个或任意多个键值对,并且同一个流中的不同元素可以包含不同数量的键值对,比如其中一个元素可以包含3个键值对,而另一个元素则可以包含5个键值对。需要注意的是,与散列以无序方式存储键值对的做法不同,流元素会以有序方式存储用户给定的键值对:用户在创建元素时以什么顺序给定键值对,它们在被取出的时候就是什么顺序。

ADD命令在成功执行时将返回新元素的ID作为结果,同一个流中的不同元素是不允许使用相同ID的,且要求新元素的ID必须比流中所有已有元素的ID都要大。在这个例子中,被返回的ID就是我们刚刚输入的ID。

流元素的ID由毫秒时间(millisecond)和顺序编号(sequcen number)两部分组成,其中使用UNIX时间戳表示的毫秒时间用于标识与元素相关联的时间,而以0为起始值的顺序编号则用于区分同一时间内产生的多个不同元素。因为毫秒时间和顺序编号都使用64位的非负整数表示,所以整个流ID的总长为128位,而Redis在接受流ID输入以及展示流ID的时候都会使用连字符-分割这两个部分。
在这里插入图片描述
用户在输入流ID的时候,除了可以给出带有毫秒时间和顺序编号的完整流ID之外,还可以给出只包含毫秒时间的不完整流ID:在这种情况下,Redis会自动将ID的顺序编号部分设置为0。

自动生成元素ID
Redis流对元素ID的要求非常严格,并且还会拒绝不符合规则的ID。因此为了方便执行添加操作,Redis为XADD命令的id参数设定了一个特殊值*:当将符号*用作id参数的值时,Redis将自动为新添加的元素生成一个可用的新ID。

自动生成的新ID会将Redis所在宿主机器当前毫秒格式的UNIX时间戳用作ID的毫秒时间,并根据当前已有ID的最大顺序编号加一来设置新ID的顺序编号部分:如在1530200000000ms内,现存最大的顺序编号为10086,那么新ID就是1530200000000-10087。

如果使用了*作为ID参数的值,但是宿主机器的当前时间比流中已有最大ID的毫秒时间要小,那么Redis将使用该ID的毫秒时间来作为新ID的毫秒时间,以此来避免机器时间倒流产生错误。

限制流的长度
XADD命令还提供了MAXLEN选项,让用户可以在添加新元素的同时删除旧元素,以此来限制流的长度,如

# 向visits中添加一个新元素,ID自动生成,且限制流的长度为5
XADD visits MAXLEN 5 * name 'tom' location '/news/77842' duration 300

在将新元素追加到流的末尾之后,XADD命令就会按照MAXLEN选项指定的长度,按照先进先出规则移除超出长度限制的元素。

  • 对流进行修剪

XTRIM命令可直接将流修剪到指定长度
语法格式:XTRIM key MAXLEN len,如

# 将visits修剪到长度为3
XTRIM visits MAXLEN 3

XTRIM命令与带有MAXLEN选项的XADD命令一样,都是根据先进先出规则来淘汰旧元素。XTRIM命令在执行之后会返回被移除元素的数量作为结果。

  • 移除指定元素

可通过XDEL命令移除指定元素,该命令接受一个流以及任意多个元素ID作为输入,并从流中移除ID对应的元素。
语法格式:XDEL key id1 id2 …,如

# 移除visits中ID为11000000000000-123456的元素
XDEL visits 11000000000000-123456

XDEL命令在成功执行之后将返回被移除元素的数量作为结果。

  • 获取流包含的元素数量

可通过XLEN命令获取流包含的元素数量
语法格式:XLEN key , 如

# 获取visits流中当前包含的元素数量
XLEN visits

如果给定的流没有包含任何元素,或者流并不存在,那么XLEN命令将返回0作为结果。

  • 获取流中的元素

正如Redis为列表LIst提供了LRANGE命令一样,Redis也为流提供了XRANGE命令,该命令可以以遍历或迭代的方式,访问流中的单个或者任意多个元素。XRANGE命令接受一个流、一个起始ID和一个结束ID以及一个可选的COUNT选项作为参数;
语法格式:XRANGE key startID endID [COUNT n],如

# 获取visits流中ID为11000000000000-123456到 11000000000000-123500中ID最小的5个元素
XRANGE visits 11000000000000-123456 11000000000000-123500 COUNT 5

XRANGE命令最简单的用法就是将命令的起始ID和结束ID设置为同一个流元素ID,这样XRANGE命令就会从流中获取并返回ID指定的元素。

XRANGE命令的起始ID和结束ID除了可以是流元素的ID之外,还可以是特殊值减号-和加号+,其中前者用于表示流中的最小ID,而后者则用于表示流中的最大ID。

除了XRANGE命令之外,还有一个XREVRANGE命令,XREVRANGE命令是XRANGE命令的逆序版本,除了会按照ID从大到小而不是从小到大的顺序访问流元素之外,其他作用是相同的。

  • 以阻塞或非阻塞方式获取流元素

XREAD命令可以以阻塞方式获取流中元素;
语法格式:XREAD [BLOCK ms] [COUNT n] STREAMS key1 key2 … id1 id2 …
其中,STREAMS选项之后的就是流的名字以及与之对应的元素ID,XREAD命令会返回大于该ID的元素,COUNT选项用于限制对于每个流最多可以返回多少个元素,BLOCK选项用于指定阻塞时间,单位为毫秒,只有给定的所有流都不存在可供读取的元素,命令才将进入阻塞状态,如

# 获取visits流中最多三个ID大于11000000000000的元素
XREAD COUNT 3 STREAMS visits 11000000000000

如果用户尝试使用XREAD命令去获取一个不存在的流,或者给定的ID超过了流中已有元素的最大ID,那么命令将返回一个空值作为结果。

只获取新出现的元素
Redis为XREAD命令提供了特殊ID参数$符号,只要在“监听”指定的流时将$符号用作ID参数的值,XREAD命令就会只获取给定流在命令执行之后新出现的元素。如

# 只在visits有新元素时返回新元素,阻塞10秒
XREAD BLOCK 10000 STREAMS visits $

三、流的消费者组

Redis流的消费者组(consumer group)允许用户将一个流从逻辑上划分为多个不同的流,并让消费者组属下的消费者去处理组中的消息。

  • 创建消费者组

创建消费者组可以通过XGROUP CREATE命令来完成,该命令是XGROUP命令的一个子命令;
语法格式:XGROUP CREATE stream groupName startID
其中,stream参数用于指定流的名字,group参数用于指定将要创建的消费者组的名字,startID用于指定消费者组在流中的起始ID,这个ID决定了消费者组要从流的哪个ID之后开始进行读取。如

# 消费者组group1以ID为4作为七点,读取流中ID大于4的所有元素
XGROUP CREATE visits group1 4

XGROUP CREATE命令目前只能为已经存在的流创建消费者组,如果用户给定的流不存在,那么命令将返回一个错误。

通过为不同的消费者组设置不同的起点ID,我们把一个流从逻辑上划分成了多个不同的流,它们包含各不相同的元素。

同一个流的消息在不同消费者组之间是共享而不是独占的,换句话说,流中的同一条消息可以被多个不同组的消费者读取,并且来自不同消费者组的读取操作不会对其他消费者组的读取操作产生任何影响。

对于一个已经存在的消费者组来说,用户可以通过执行XGROUP SETID命令来为消费者组设置新的最后递送消息ID。语法格式:XGROUP SETID stream group id

  • 读取消费者组

通过XREADGROUP命令可以读取消费者组中的消息;
语法格式:XREADGROUP GROUP group consumer [COUNT n] [BLOCK ms] STREAMS stream1 stream2 … id1 id2 …

# 以消费者worker1读取visits流中group1消费者组,起始ID为0
XREADGROUP GROUP group1 worker1 STREAMS visits 0

这个命令的基本参数及作用与XREAD命令大同小异,主要区别在于新增的GROUP group consumer选项,该选项的两个参数分别用于指定被读取的消费者组以及负责处理消息的消费者。

从逻辑上来说,消费者就是负责处理消息的客户端。与创建消费者组不一样,消费者不用显式地创建,用户只要在执行XREADGROUP命令时给定消费者的名字,Redis就会自动为新出现的消费者创建相应的数据结构。每当用户使用XREADGROUP命令读取出一条消息,并将这条消息指派给一个消费者处理时,该消费者就会把所指派的消息添加到自己的待处理消息队列中。

需要注意的是,与多个消费者组能够共享同一个流中的元素不一样,同一消费者组中的每条消息只能有一个消费者,换句话说,不同的消费者将独占组中的不同消息:当一个消费者读取了组中的一条消息之后,同组的其他消费者将无法读取这条消息。

前面在介绍XREAD命令时曾经提到过,可以通过将id参数的值设置为$,在不知道最后一条消息的ID的情况下,获取新出现的消息。XREADGROUP命令同样可以执行类似的操作,只要将id参数的值设置为特殊符号>,命令就会自动地向消费者返回尚未递送过的新消息。

消息的状态转换
当消费者处理完一条消息后,需要向Redis发送一条针对该消息的XACK命令;
语法格式:XACK stream group id1 id2 …
当Redis接收到消费者发来的XACK命令之后,就会从消费者组的待处理消息队列以及消费者的待处理消息队列中移除指定的消息。这样一来,这些消息的状态就会从“待处理”转换为“已确认”,以此来表示消费者已经处理完这些消息了。

一条消费者组消息从出现到处理完毕,需要经历以下阶段:
·首先,当一个生产者通过XADD命令向流中添加一条消息时,该消息就从原来的“不存在”状态转换成了“未递送”状态。
·然后,当一个消费者通过XREADGROUP命令从流中读取一条消息时,该消息就从原来的“未递送”状态转换成了“待处理”状态。
·最后,当消费者完成了对消息的处理,并通过XACK命令向服务器进行确认时,该消息就从原来的“待处理”状态转换成了“已确认”状态。
在这里插入图片描述
在执行读取操作之后,可以通过XPENDING命令以及XINFO GROUPS命令查看消费者组的相关信息,其中XPENDING命令用于列出消费者组目前待处理消息的相关信息,XINFO GROUPS命令则用于列出给定流相关联消费者组的相关信息,除此之外,XINFO CONSUMERS命令用于打印指定消费者组的所有消费者及其相关信息,以及XINFO STREAM命令用于打印给定流的相关信息;
语法格式:
XPENDING stream group
XINFO GROUPS stream
XINFO CONSUMERS stream group
XINFO STREAM stream

  • 删除消费者

当不需要某个消费者的时候,可以通过XGROUP DELCONSUMER命令将其删除;
语法格式:XGROUP DELCONSUMER stream group consumer

命令在执行之后将返回一个数字作为结果,这个数字就是消费者被删除时,它仍在处理的消息数量。为了避免消息丢失,在删除一个消费者之前应该确保递送给它的所有消息均已处理完毕,或者使用XCLAIM命令显式地转移待处理消息的归属权。

  • 删除消费者组

当一个消费者组完成了它的任务之后,可以通过执行XGROUP DESTROY命令来将其删除;
语法格式:XGROUP DESTROY stream group
命令在成功执行时返回1,因为组不存在等原因导致命令执行失败时返回0。
与XGROUP DELCONSUMER命令一样,为了保证程序的正确性,用户需要保证在删除消费者组的时候,组中已经没有任何待处理消息,否则这些待处理消息可能无法得到妥善的处理。

  • 转移消息的归属权

可以通过XCLAIM命令,将指定消息的归属权从一个消费者转向另一个消费者;
语法格式:XCLAIM stream group new_consumer max_pending_time id1 id2 id3 … [JUSTID]
其中stream参数和group参数用于指定消息所在的流和消费者组,new_consumer指定了消息的新消费者,而命令中的任意多个id参数则指明了需要转移归属权的消息。max_pending_time参数指定了执行归属权转移操作所需的最大消息处理时限,单位为毫秒。
具体来说:
·如果XCLAIM命令执行的时候,消息原来的消费者用在处理该消息上的时间已经超过了指定的时限,那么归属权转移操作就会被执行。
·与此相反,如果原消费者处理该消息的时间并未超过给定的时限,或者该消息已经被原消费者确认,那么归属权转移操作将放弃执行。
例如

# 如果消息1535002039330-0现在的消费者处理该消息的时间超过了60000ms,
# 那么将该消息的归属权转移给消费者worker2。
XCLAIM stream1 group1 worker2 60000 1535002039330-0

XCLAIM命令在成功执行之后将会返回被转移的消息作为结果;相反,如果转移操作因为处理时限未到等原因而未能顺利执行,那么命令将返回一个空列表。但如果有需要的话,用户也可以通过给定可选的JUSTID选项,让命令只返回被转移消息的ID。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值