新的数据结构Stream
Redis5.0引入了新的数据结构stream,依据文档说明它是一个类日志文件的数据结构,而日志文件结构是一个单纯的追加文件模式,存在许多限制(单纯的追加模式是无法满足我们多变的功能需求的);Redis的stream中加入了一系列的阻塞操作和Consumers Group(消费者组)的概念,这一概念和kafka的消费者组是基本相同的,允许一组消费者协同消费。
1.Stream操作
官方文档中介绍了以下几个操作命令:
Because streams are an append only data structure, the fundamental write command, called XADD, appends a new entry into the specified stream. A stream entry is not just a string, but is instead composed of one or multiple field-value pairs. This way, each entry of a stream is already structured, like an append only file written in CSV format where multiple separated fields are present in each line.
因为streams是一个仅追加模式数据结构,基本的写操作是:XADD,追加一个新的entry到指定的stream。一个stream entry不单单是一个字符串,儿是有一对或者多对field-value(键值)组成。stream中的每一个entry都是已经结构化的,类似向CSV格式文件中追加,逐行多分割字段。
XADD mystream * sensor-id 1234 temperature 19.8
command_name stream_name entryID field1 value1 field2 value2
1518951480106-0
下面重点来看一下XADD操作的返回值,自生成实体ID(auto-generated entry ID):
构成
<millisecondsTime>-<sequenceNumber>
millisecondsTime: 本地Redis节点本地时间,如果当前的毫秒数比前一个entryid中的毫秒数小,则会使用前一个entryid中的毫秒数。
sequenceNumber:同1毫秒内追加的entry个数,64位,理论上是不会超出的
以上是Redis服务器自增entryid的构成,处于某些原因用户可能想要在不基于毫秒的情况下指定entryid(如:0-1)来替换通配id(*),原则是不能大于前一个entryid。
获取数据
stream获取数据可以分为三种模式:
- 常规(可阻塞)获取 XREAD
- 按照事件范围获取 XRANGE XREVRANGE
- 通过消费者组消费stream数据
//以下分别是阻塞读取stream数据和根据时间范围获取数据的命令示例
> XREAD COUNT 2 STREAMS mystream 0 //阻塞读取需要增加BLOCK参数
> XREAD BLOCK 0 STREAMS mystream $
1) 1) "mystream"
2) 1) 1) 1519073278252-0
2) 1) "foo"
2) "value_1"
2) 1) 1519073279157-0
2) 1) "foo"
2) "value_2"
> XRANGE mystream - +
1) 1) 1518951480106-0
2) 1) "sensor-id"
2) "1234"
3) "temperature"
4) "19.8"
2) 1) 1518951482479-0
2) 1) "sensor-id"
2) "9999"
3) "temperature"
4) "18.2"
XREAD
- Stream可以有多个客户端(消费者)等待获取数据。每一个新的实体会默认发送给等待数据的每一个消费者。这个行为不同于list的阻塞,每个消费者会获取到不同地实体。这类似发布订阅模式。
- list中的发布订阅使用的pop命令,元素最终会被删除。在stream中不同地是元素会被无限期的追加在stream中,除非用户明确声明要删除。不同地消费者会通过记录上一个实体的id来知道什么是新消息。
- $ 符号代表将stream中最大entryid作为上一次获取的id,也就意味着只读取最新消息。
XRANGE
-和+符号代表最小和最大的entryid
Consumer Groups 消费者组
消费者组是为了解决同一stream中不同地消息子集不同客户端消费的场景。下面看一下官方提供的场景:
想象一下,我们有三个消费者C1,C2,C3,还有一个包含7个元素的stream:1,2,3,4,5,6,7;之后我们想让当前stream中的消息按照下图方式消费:
1 -> C1
2 -> C2
3 -> C3
4 -> C1
5 -> C2
6 -> C3
7 -> C1
为了达到这种目的,Redis使用了消费者组这个概念,它同kafka的消费者组没有任何关系,仅仅是概念上的相似。一个消费者组类似一个从stream获取数据的假的消费者,它实际上服务于多个消费者,提供一定保障:
- 一条信息只属于一个消费者,不会被多个消费者消费。
- 消费者组内的消费者具有唯一,大小写敏感的名称。
- 消费一条消息需要通过特定的命令告知已被正确消费,可以从服务端移除
- 消费者组跟踪所有信息的状态,被消费但是没有确认正确消费的信息;由于这个特性,当查看历史信息的时候每个消费者只能看到自己的信息。
官方提供了一种消费者组的表达:
+----------------------------------------+
| consumer_group_name: mygroup |
| consumer_group_stream: somekey |
| last_delivered_id: 1292309234234-92 |
| |
| consumers: |
| "consumer-1" with pending messages |
| 1292309234234-4 |
| 1292309234232-8 |
| "consumer-42" with pending messages |
| ... (and so forth) |
+----------------------------------------+
//以下为对照
+----------------------------------------+
| 消费者组名称: mygroup |
| 消费者组流: somekey |
| 最后消费的信息id: 1292309234234-92 |
| |
| 所有消费者: |
| "consumer-1" with pending messages |
| 1292309234234-4 |
| 1292309234232-8 |
| "consumer-42" with pending messages |
| ... (and so forth) |
+----------------------------------------+
**XGROUP** 创建销毁及管理消费者组
**XREADGROUP** 通过消费者组读取信息
**XACK** 消费者标记信息状态的命令
永久故障修复
现实情况中消费者可能存在永久宕机且无法恢复的情况。Redis对于这种情况提供了一种特性,能够在消费者宕机时将消息重新指定到其他消费者。
实现这个特性需要通过特定命令XPENDING,这个命令为只读安全的,只需提供两个参数,stream名称和消费者组名称。
以下是命令构成:
第一步查看pending的消息:
XPENDING <key> <groupname> [<start-id> <end-id> <count>
[<consumer-name>]]
第二步恢复消息使得其他消费者可以消费pending消息:
XCLAIM <key> <group> <consumer> <min-idle-time> <ID-1> <ID-2> ... <ID-N>
查看流信息
> XINFO HELP
1) XINFO <subcommand> arg arg ... arg. Subcommands are:
2) CONSUMERS <key> <groupname> -- Show consumer groups of group <groupname>. --显示消费者组中的消费者
3) GROUPS <key> -- Show the stream consumer groups. --显示消费者组
4) STREAM <key> -- Show information about the stream. --显示流信息
5) HELP -- Print this help.
零长度流
当stream中没有元素时不会被删除
特殊的id
在此总结一下各命令中用到的特殊id:
- 最小id
+ 最大id
$ 最后消费实体的id,之后再消费都是最新的
> XREADGROUP命令使用时,代表消费未消费的信息
* 仅与XADD同时使用,让redis为我们自动生成id
以上仅仅时官方文档中对stream这个数据结构所作的介绍,不包含具体开发过程中的使用方法;其实java开发中,目前仅lettuce和Redisson支持stream特性,以上文档部分是在学习官方文档的过程中边看边记录的,限于我的学习程度和理解水平,没什么深度可言,且可能会有错误的地方,如有意见望不吝赐教,必及时改正!