Redis stream 方案
#1634
Replies: 1 comment
-
需求原始是想从项目从redis迁移到pika, 原来是用了redis的mq机制,不需要严格的mq服务器那种的,希望pika有这块的实现能迁移过来就很ok了 |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
原文档链接:https://t8dj523v5p.feishu.cn/wiki/YRmJwKI5oiPYtLkiQfcczmd2ndd
by 周开颜
这个 stream 功能对于 redis 来说是相对比较独立的,和其它模块耦合性非常低,同理 pika 应该也一样
实现需求上的疑问
实现原则
是做一个允许消息丢失的mq(比如 redis),还是一个百分百可靠的mq?
需要实现的指令
具体要做到什么程度,是要做到跟 redis 一样的功能以及兼容其所有指令么?(redis stream 的实现代码,除了数据结构和测试代码,本身有 4000 多行,工作量初步看起来其实有点大,如果真的有这个需求,我可以当作一个长期的 issue 去做)
参考 redis 的指令,总共有 21 条指令(https://redis.io/commands/?group=stream)
其中核心指令有以下几条
其中比较关键的概念是
消费组
,每个组可以互相独立地消费消息,但组内的成员对消息是竞争消费关系。指令具体使用以及详细解析:
redis stream 实现原理
宏观结构
宏观上来看所谓消息队列其实就是一个 List,链头通过插入的方式发布消息,链尾则以向前遍历的方式进行消费。
通常来说,消息队列除了队列的性质,还需要支持对消息的快速查找,所以一般的实现方式都是一个类似于跳表的形式加上索引结构。前者用于保持队列的性质,后者用于低时间复杂度的查找。
数据结构:radix tree
redis stream 中,针对每一条消息,都必须设置唯一且递增的消息 ID。
这种一段时间内的连续 ID,前缀都有一些高度重复性,所以用这类前缀树可以有效节约空间使用率。
除了用于存储消息,同 hash_map 的使用方式类似,它还被用来存储以下关系。
数据结构:listpack
*A lists of strings serialization format:*一个字符串列表的序列化格式,也就是将一个字符串列表进行序列化存储。
简单的理解 listpack 就是一款专门为
节省内存空间
,通过特定的编码方式将数据进行编码和解码的数据结构,这种结构天生就是为节省空间而存在的。总的来看,每个 stream 都用对应一个 radix tree,value 则存储了一个指向 listpack 的指针,同一个 listpack 可能存储多个消息的 value。
新的消息条目会被添加到Listpack中。如果添加新条目后的Listpack大小超过了设定的限制,那么会从Radix Tree中分裂出一个新的Listpack来存储超出的数据。
多播支持
实际上就是对消费组的支持,主要需要注意以下细节:
持久化方案
理论上需要将 stream 和其用到的数据结构对 RDB 和 AOF 做支持。
RDB 支持:
参考博客
pika 具体实现方式(实现文档初稿,进行中)
命令注册
所有命令在以下文件夹中与cmd_table绑定(以 LPUSH 为例)
其次在下方文件声明对应的命令,继承于 Cmd 类
最终通过 Do 函数,传递到命令的具体实现位置
(原来 wiki上有现成的 https://www.cnblogs.com/sigma0-/p/12831546.html)
实现位置
Stream 类算是一个存储抽象,应该是在 storage 中实现?
基础数据结构
Pika 的存储结构
疑问:代码里面并没有看到 blackwidow 相关的概念。倒是大致看到了以下的结构:
从存储的实现上来看,最底层是一个叫做
Redis
的类,在Redis的基础上,分别派生出了string
,set
,lists
,hash
,zset
几个存储结构,它们的实现都是基于 RocksDB 的实例:在一个 storage 抽象中会同时拥有上述五种存储的实例(这个也和文档上的 blackwindow 概念相似)
命令在解析和执行时,有个关键的步骤用于获取 storage 实例。后续命令的实现,会从 slot 获取具体的 storage,并执行相应的操作。
Stream 的结构
stream 初步来看有三种实现方式,前两种实现方式较为现实
直接用 blackwidow 实现的结构,比如 List 和 Hash
类似 blackwidow 的实现,直接在 RocksDB 上封装一层实现?
抠 rax-tree 和 listpack,或者手写数据结构。
实现细节
命令解析
应该有大部分现成的解析代码,只需要正确链接到指令对应的实现函数就行
主要功能
我理解的主要功能包含:
Group => Stream
的映射,需要用一个可持久化的 hash 结构来实现,redis 中使用的是 ratix tree, pika 中可能可以用 RocksDB 来存储。除此之外,还需要存储一些相关的元数据。Consumer => Group
的映射,以及一些元数据。持久化
如果用 RocskDB 实现,应该不用直面持久化的问题,但是暂时还不知道 pika 恢复的流程,stream 相关的元数据也要额外考虑。
兼容主从复制
注意到每条命令都有一个如下的函数,应该是用于生成 binlog 的函数,应该需要自己实现?
既然 Binlog 有了,按道理主从复制的逻辑不会和 Stream 的具体实现耦合度太高。
Beta Was this translation helpful? Give feedback.
All reactions