Stream 数据流的使用越来越多,Redis 的作者 antirez 也在积极思考,如何让 redis 能够很好的支持数据流的使用场景
antirez 认为 Redis 现有的数据结构都不能很好的处理数据流,例如:
(1)Sorted sets
有序集合中的元素根据他们的分值进行变化,不能自然的模拟不断被传递的消息,也不支持 client 阻塞等待新消息
(2)Lists
列表中的阻塞形式是一个元素对一个 client,并且 list 中的元素没有一个固定标识
(3)Pub/Sub
发布订阅适合一对多的场景,但元素是很快消失的,这样不符合某些场景的需求,例如需要保存历史数据的情况、连接断掉重连后需要重新获取消息的情况,再比如时间序列,范围查询很重要,如查询过去10秒钟我体温的监测数据
antirez 试图解决这些问题,例如是否可以给 Pub/Sub 添加历史记录、是否可以给 List 添加灵活的访问模式,但都不太理想,antirez 最后决定使用新的数据结构
antirez 受到一个 log 类型的 Redis module 启发,决定使用 log 来实现
log 是一个极其普通的东西,我们每个人都在用,就是打开文件以某种格式把数据追加进去
普通日志的 offset 是没有逻辑意义的,而且不好做垃圾回收,追加形式的日志很难移除旧数据,理想的形式是,指定一个数字,之前的老日志就被删除了
antirez 在开发 Redis Cluster 时使用的 radix 树正好为 Redis Stream 打下了很好的基础,可以达到空间和访问时间都很高效
antirez 也研究了 kafka 的思路(kafka 就是基于 log 的),借鉴了一些好的概念,例如 consumer groups
antirez 希望 Redis streams 能在事件、消息型应用中发挥重要作用,尤其是在 time series 场景中
> XADD mystream * sensor-id 1234 temperature 10.5
1506871964177.0
XADD 命令会把新元素添加到指定的 stream 中
这个例子中,mystream
是目标 stream,新的元素有2个 field,sensor-id
和 temperature
同一个 stream 中的不同元素中的 field 是可以不同的,但使用相同的 field name 可以更有效的利用内存
XADD 的返回值是新插入的元素ID,例子中的 *
表示让 XADD 自动生成一个 ID,当然也可以自己指定一个 ID
ID 由2部分构成:毫秒值时间戳 + 序号,用 .
分隔,序号用来区分相同时间新加的元素
时间戳来自2方面,一是 Redis Server 本机的系统时间,二是 stream 中元素的最大时间值,生成 ID 时,会选取二者中的最大值,例如本机的时间被调小了,那么还可以保证ID是在继续增长的
> MULTI
OK
> XADD mystream * foo 10
QUEUED
> XADD mystream * bar 20
QUEUED
> EXEC
1) 1506872463535.0
2) 1506872463535.1
这个例子展示了批量快速插入,也可以看到不同元素中的 field 可以不同
> XRANGE mystream 1506871964177.0 1506871964177.0
1) 1) 1506871964177.0
2) 1) "sensor-id"
2) "1234"
3) "temperature"
4) "10.5"
XRANGE
命令用于从指定 stream 中获取某个范围的元素,参数分别是 stream 名称、开始位置、结束位置,开始和结束位置的元素是包括在内的
Redis streams 将使 Redis 覆盖更多的使用场景,其中一个重要场景就是 time series,会在 4.0 系列版本中发布,大概是在年底,目前核心功能已经开发完成,有兴趣的话可以获取 Github 上的 streams 分支感受一下