S11赛季幻翎洛推荐搭配什么符文?如何出装?浆果工厂大亨手游下载焦点趣致集团港股IPO:为快销品品牌提供营销服务,上半年扭亏为盈净利约6500万时尚枪支建造与运行游戏下载综合旗下首款纯电车型,理想MEGA低伪装谍照曝光:12月首发

Redis Stream 数据结构实现原理真的很强

  • 焦点
  • 时尚
  • TypeScript被放弃!又一知名前端利器决意转回JS,社区不满:这在开倒车!
  • 综合

Redis Stream 数据结构实现原理真的很强

Redis Stream 数据结构实现原理真的很强

  • 美团闪购与Apple授权店持续深化合作,321城近5000家门店入驻
  • 最好用的视频分割软件+视频合并软件合集[EXE]百度云网盘下载 – 好样猫
  • 模拟游戏《游戏开发的二三事》上架Steam
  • 娱乐
  • 迷你生活魔法王国下载
  • 2023-09-21 16:19:56

Redis 据结Stream 数据结构落成道理真的很强

故故者:码哥 数据库 Redis Stream 是 Redis 5.0 版本特意为音问队列设计的数据规范,鉴戒了 Kafka 的构落 Consume Group 设计脉络,提供了销耗组概记挂。成道六一网同期提供了音问的据结耐久化和主从复制机制,客户端能够造访任何期间的构落数据,而且能记着每一个客户端的成道造访职位,从而担保音问不销耗。据结

你好,构落我是成道码哥,一个拥抱硬核身手和目的据结,面向黎民币编程的构落须眉,配阁阁星标不迷途。成道

我在【Redis 据结骗捏 List 落成音问队列的利与弊】说过骗捏 List 落成音问队列有许多限度性。

  • 莫得 ACK 机制。构落
  • 莫得访佛 Kafka 的成道 ConsumerGroup 销耗组概记挂。
  • 音问齐集。
  • List 是线性结构,究诘指定数据须要遍历通盘列表。

1、是什么

Stream 是 Redis 5.0 版本特意为音问队列设计的数据规范,鉴戒了 Kafka 的 Consume Group 设计脉络,提供了销耗组概记挂。

同期提供了音问的耐久化和主从复制机制,客户端能够造访任何期间的数据,而且能记着每一个客户端的造访职位,从而担保音问不销耗。

以下几个是 Stream 规范的症结特质。

  • 骗捏 Radix Tree 和 listpack 结构来存储音问。
  • 音问 ID 序列化生成。
  • 鉴戒 Kafka Consume Group 的六一网概记挂,多个销耗者区分赴任异的 Consume Group 中,销耗统一个 Streams,统一个 Consume Group 的多个销耗者能够通盘并行但不重复销耗,擢升销耗能力。
  • 赞助多播(多对多),阻难和非阻难读捏。
  • ACK 证明机制,担保了音问起码被销耗一次。
  • 可配阁阁音问覆灭上限阈值,我会把汗青音问阵殁,预防内存占用过大。

须要瞩目的是,Redis Stream 是一种超轻量级的 MQ,并莫得无缺落成音问队列的集体设计重点,是以它的骗捏处景须要酌量贸易的数据量和对职能、靠得住性的须要。

适当编制音问量不大,忍受数据销耗,骗捏 Redis Stream 行径音问队列就能纳福高职能连辛苦读写音问的优势

2、修齐心法

每个 Stream 都有一个独一的称谓,行径 Stream 在 Redis 的 key,在首次骗捏 xadd 指示补充音问的韶华会自愿设立。

能够顾念记挂到 Stream 在一个 Redix Tree 树上,树上存储的是音问 ID,每个音问 ID 对应的音问通过一个指针指向 listpack。

Stream 流就像是一个仅追加本色的音问链表,把音问一个个串起来,每个音问都有一个独一的 ID 和音问本色,音问本色则由多个 field/value 键值对组成。底层骗捏 Radix Tree 和 listpack 数据结构存储数据。

为了便于领路,我画了一张图,并对 Radix Tree 的存储数据做了下变形,骗捏列表来阐述 Stream 中音问的逻辑有序性。

这张图触及许多概记挂,然而你不要慌。我一步步拆户口说,终末你再追想顾念记挂就懂了。

先带你屡下集体脉络。

  • Consumer Group:销耗组,每个销耗组能够有一个能够多个销耗者,销耗者之间是较量说合。迥异销耗组的销耗者之间无任何说合。
  • *pel,全称是 Pending Entries List,记载了面前被客户端读捏然而还莫得 ack(Acknowledge character 证明字符)的音问。要是客户端莫得 ack,这个变量的音问 ID 会越来越多。这是一个重点数据结构,用来确保客户端起码销耗音问一次。

Stream 结构

Streams 结构的源码界说在 stream.h 源码华厦 stream 结构体中。

typedef struct stream {     rax *rax;    uint64_t length;    streamID last_id;    streamID first_id;    streamID max_deleted_entry_id;    uint64_t entries_added;    rax *cgroups;} stream;typedef struct streamID {     uint64_t ms;    uint64_t seq;} streamID;
  • *rax,是一个 rax 的指针,指向一个 Radix Tree,key 存储音问 ID,value 本质上指向一个 listpack 数据结构,存储了多条音问,每条音问的 ID 都大于等于 这个 key 的音问 ID。
  • length,该 Stream 的音问条数。
  • streamID结构体,音问 ID 笼统,集体占 128 位,里面维持了毫秒期间戳(字段 ms);一个毫秒内的自增序号(字段 seq),用于鉴识统一毫秒内插入多条音问。
  • last_id,面前 Stream 终末一条音问的 ID。
  • first_id,面前 Stream 第一条音问的 ID。
  • max_deleted_entry_id,面前 Stream 被苟简的最大的音问 ID。
  • entries_added,通盘有多少条音问补充到 Stream 中,entries_added = 已苟简音问条数 + 未苟简音问条数。
  • *cgroups,rax 指针,也指向一个 Radix Tree ,记载面前 Stream 的集体 Consume Group,每个 Consume Group 的称谓都是独一记号,行径 Radix Tree 的 key,Consumer Group 实例行径 value。

Consumer Group

Consumer Group 由 streamCG 结构体界说,每个 Stream 能够有多个 Consumer Group,一个销耗组能够有多个销耗者同期对组内音问进行销耗。

/* Consumer group. */typedef struct streamCG {     streamID last_id;    long long entries_read;    rax *pel;    rax *consumers;} streamCG;
  • last_id,露出该销耗组的销耗者曾经读捏但还未 ACK 的终末一条音问 ID。
  • *pel,是 pending entries list 简写,指向一个 Radix Tree 的指针,覆灭着 Consumer group 中集体销耗者读捏但还未 ACK 证明的音问,就是这玩意落成了 ACK 机制。该树的 key 是音问 ID,value 联系一个 streamNACK 实例。
  • *consumers, Radix Tree 指针,露出销耗组华厦集体销耗者,key 是销耗者称谓,value 指向一个 streamConsumer 实例。

streamNACK

streamCG -> *pel 对应的 value 是一个 streamNACK 实例,用于笼统销耗者曾经读捏,然而未 ACK 的音问 ID 相故故音问。

/* Pending (yet not acknowledged) message in a consumer group. */typedef struct streamNACK {     mstime_t delivery_time;    uint64_t delivery_count;    streamConsumer *consumer;} streamNACK;
  • delivery_time,该音问终末一次推送给 Consumer 的期间戳。
  • delivery_count,音问被推送次数。
  • *consumer,音问推送的 Consumer 客户端。

streamConsumer

Consumer Group 中对 Consumer 的笼统。

/* A specific consumer in a consumer group.  */typedef struct streamConsumer {     mstime_t seen_time;    sds name;    rax *pel;} streamConsumer;
  • seen_time,销耗者迩来一次被激活的期间戳。
  • name,销耗者称谓。
  • *pel, Radix Tree 指针,马虎统一个音问而言,``streamCG -> pel与streamConsumer -> pel的streamNACK` 实例是统一个。

终末来一张图,便于你领路。

肖材积:“Redis 你好,Stream 若何麇集 Radix Tree 和 listpack 结构来存储音问?为什么不骗捏散列表来存储,音问 ID 行径散列表的 key,散列表的 value 存储音问键值对本色。’”

在答复曾经,先插入几条音问到 Stream,让你对 Stream 音问的存储花色有个大领路知。

该夂箢的语法如下。

XADD key id field value [field value ...]

Stream 华厦每个音问能够贮蓄迥异数量的多个键值对,写入音问获胜后,我会把音问的 ID 返回给客户端。

践诺如下指示把用户进货竹帛的下单音问寄阁阁到 hotlist:books队列,音问本色症结由 payerID、amount 和 orderID。

> XADD hotlist:books * payerID 1 amount 69.00 orderID 91679218539571-0> XADD hotlist:books * payerID 1 amount 36.00 orderID 151679218572182-0> XADD hotlist:books * payerID 2 amount 99.00 orderID 881679218588426-0> XADD hotlist:books * payerID 3 amount 68.00 orderID 801679218604492-0

hotlist:books 是 Stream 的称谓,背面的 “*” 露出让 Redis 为插入的音问自愿生成一个独一 ID,你也能够自界说。

音问 ID 由两部门组成。

  • 面前毫秒内的期间戳。
  • 顺次编号。从 0 为起点值,用于鉴识同临期间内生长的多个夂箢。

肖材积:“若何领路 Stream 是一种只践诺追加独霸(append only)的数据结构?”

通过将元素 ID 与期间进行联系,并逼迫请求新元素的 ID 必须大于旧元素的 ID, Redis 从逻辑上将 Stream 酿成了一种只践诺追加独霸(append only)的数据结构。

用户能够肯定,新的音问和变乱只会呈而今已有音问和变乱之后,就像现实寰宇里新变乱老是爆发在已有变乱之后雷同,齐备都是有序进行的。

肖材积:“插入的音问 ID 大部门雷同,譬喻这四条音问的 ID 都是 1679218 前缀。此外,每条音问键值对的键通常都是雷同的,譬喻这四条音问的键都是 payerID、amount 和 orderID。骗捏散列表存储的话会许多冗尾数据,你这样抠门,是以不骗捏散列表对造故故?”

没缺陷,小老弟很颖慧。为了节约内存,我骗捏了 Radix Tree 和 listpack。Radix Tree 的 key 存储音问 ID,value 骗捏 listpack 数据结构存储多个音问, listapck 华厦音问 ID 都大于等于 key 存储的音问 ID。

我在前面曾经道过 listpack,这是一个紧凑型列表,额外节约内存。而 Radix Tree 数据结构的最大特质是适当覆灭拥有雷同前缀的数据,从而达到节约内存。

终竟 Radix Tree 是若何的数据结构,连缀往下顾念记挂。

Radix Tree

Radix Tree,也被称为 Radix Trie,能够 Compact Prefix Tree),用于高效地存储和查找字符串齐集。它将字符串遵照前缀拆分红一个个字符,并将每个字符行径一个节点存储在树中。

当插入一个键值对时,Redis 会将键遵照字符拆分红一个个字符,并遵照字符在 Radix tree 华厦职位找到适当的节点,要是该节点不糊口,则设立新节点并补充到 Radix tree 中。

当集体字符都补充结束后,将值目的指针覆灭到终末一个节点中。当究诘一个键时,Redis 遵照字符顺次遍历 Radix tree,要是露出某个字符不糊口于树中,则键不糊口;否则,要是终末一个节点露出一个齐备的键,则返回对应的值目的。

如下图露出一个轻省的前缀树,将根节点到叶子节点的阶梯对应字符拼接起来,就赢得了两个 key(“他说碉堡了”、“他说碉炸了”)。

你该当露出了,这两个 key 领有群众前缀(他说碉),前缀树落成了分享骗捏,这样就能够预防雷同字符串重复存储。要是采纳散列表的覆灭花式,阿谁 key 的雷同前缀就会被频频存储,招致内存虚耗。

Radix Tree 革新

每个节点只覆灭一个字符,一是会虚耗内存空间,二是在进行究诘时,还须要逐个结婚每个节点露出的字符,对究诘职能也会酿成训诲。

是以,Redis 并莫得直接骗捏范例前缀树,而是做了一次变种——Compact Prefix Tree(缩短前缀树)。通常来说,当多个 key 拥有雷同的前缀时,那就将雷同前缀的字符阿谀结在一个分享节点中,从而减轻存储空间。

如下几个 key(test、toaster、toasting、slow、slowly)在 Radix Tree 上的结构。

鉴于 Compact Prefix Tree 能够分享雷同前缀的节点,是以在存储一组拥有雷同前缀的键时,Redis 的 Radix tree 比此外数据结构(如哈希表)拥有更低的空间耗尽和更快的究诘速率。

Radix Tree 节点的数据结构由 rax.h文献华厦 raxNode 界说。

typedef struct raxNode {     uint32_t iskey:1;    uint32_t isnull:1;    uint32_t iscompr:1;    uint32_t size:29;    unsigned char data[];} raxNode;
  • iskey:从 Radix Tree 根节点到面前节点组成的字符串是否是一个齐备的 key。是的话 iskey 的值为 1。
  • isnull:面前节点是否为空节点,要是面前节点是空节点的话,就不须要为该节点分配指向 value 的指针内存。
  • iscompr,是否为缩短节点。
  • size,面前节点的大小,险些指会遵照节点规范而迁移。要是是缩短节点,该值露出缩短数据的长度;要是黑白缩短节点,该值露出节点的子节点个数。
  • data[],本质存储的数据,遵照节点规范迥异而有所迥异。
  • 缩短节点,data 数据蕴涵子节点对应的字符、指向子节点的指针,节点为终竟 key 对应的 value 指针。
  • 缩短节点,data 数据贮蓄子节点对应的连续字符串、指向子节点的指针,以及节点为终竟 key 的 value 指针。
  • value 指针指向一个 listpack 实例,里面覆灭了音问本质本色。

Radix Tree 最大的特质就是适当覆灭拥有雷同前缀的数据,落成节约内存的目的,以及赞助限定查找。而这个就是 Stream 采纳 Radix Tree 行径底层数据结构的起因。

责任编辑:姜华 源泉: 码哥字节 Redis音问队列


Redis 据结Stream 数据结构落成道理真的很强

故故者:码哥 数据库 Redis Stream 是 Redis 5.0 版本特意为音问队列设计的数据规范,鉴戒了 Kafka 的构落 Consume Group 设计脉络,提供了销耗组概记挂。成道六一网同期提供了音问的据结耐久化和主从复制机制,客户端能够造访任何期间的构落数据,而且能记着每一个客户端的成道造访职位,从而担保音问不销耗。据结

你好,构落我是成道码哥,一个拥抱硬核身手和目的据结,面向黎民币编程的构落须眉,配阁阁星标不迷途。成道

我在【Redis 据结骗捏 List 落成音问队列的利与弊】说过骗捏 List 落成音问队列有许多限度性。

  • 莫得 ACK 机制。构落
  • 莫得访佛 Kafka 的成道 ConsumerGroup 销耗组概记挂。
  • 音问齐集。
  • List 是线性结构,究诘指定数据须要遍历通盘列表。

1、是什么

Stream 是 Redis 5.0 版本特意为音问队列设计的数据规范,鉴戒了 Kafka 的 Consume Group 设计脉络,提供了销耗组概记挂。

同期提供了音问的耐久化和主从复制机制,客户端能够造访任何期间的数据,而且能记着每一个客户端的造访职位,从而担保音问不销耗。

以下几个是 Stream 规范的症结特质。

  • 骗捏 Radix Tree 和 listpack 结构来存储音问。
  • 音问 ID 序列化生成。
  • 鉴戒 Kafka Consume Group 的六一网概记挂,多个销耗者区分赴任异的 Consume Group 中,销耗统一个 Streams,统一个 Consume Group 的多个销耗者能够通盘并行但不重复销耗,擢升销耗能力。
  • 赞助多播(多对多),阻难和非阻难读捏。
  • ACK 证明机制,担保了音问起码被销耗一次。
  • 可配阁阁音问覆灭上限阈值,我会把汗青音问阵殁,预防内存占用过大。

须要瞩目的是,Redis Stream 是一种超轻量级的 MQ,并莫得无缺落成音问队列的集体设计重点,是以它的骗捏处景须要酌量贸易的数据量和对职能、靠得住性的须要。

适当编制音问量不大,忍受数据销耗,骗捏 Redis Stream 行径音问队列就能纳福高职能连辛苦读写音问的优势

2、修齐心法

每个 Stream 都有一个独一的称谓,行径 Stream 在 Redis 的 key,在首次骗捏 xadd 指示补充音问的韶华会自愿设立。

能够顾念记挂到 Stream 在一个 Redix Tree 树上,树上存储的是音问 ID,每个音问 ID 对应的音问通过一个指针指向 listpack。

Stream 流就像是一个仅追加本色的音问链表,把音问一个个串起来,每个音问都有一个独一的 ID 和音问本色,音问本色则由多个 field/value 键值对组成。底层骗捏 Radix Tree 和 listpack 数据结构存储数据。

为了便于领路,我画了一张图,并对 Radix Tree 的存储数据做了下变形,骗捏列表来阐述 Stream 中音问的逻辑有序性。

这张图触及许多概记挂,然而你不要慌。我一步步拆户口说,终末你再追想顾念记挂就懂了。

先带你屡下集体脉络。

  • Consumer Group:销耗组,每个销耗组能够有一个能够多个销耗者,销耗者之间是较量说合。迥异销耗组的销耗者之间无任何说合。
  • *pel,全称是 Pending Entries List,记载了面前被客户端读捏然而还莫得 ack(Acknowledge character 证明字符)的音问。要是客户端莫得 ack,这个变量的音问 ID 会越来越多。这是一个重点数据结构,用来确保客户端起码销耗音问一次。

Stream 结构

Streams 结构的源码界说在 stream.h 源码华厦 stream 结构体中。

typedef struct stream {     rax *rax;    uint64_t length;    streamID last_id;    streamID first_id;    streamID max_deleted_entry_id;    uint64_t entries_added;    rax *cgroups;} stream;typedef struct streamID {     uint64_t ms;    uint64_t seq;} streamID;
  • *rax,是一个 rax 的指针,指向一个 Radix Tree,key 存储音问 ID,value 本质上指向一个 listpack 数据结构,存储了多条音问,每条音问的 ID 都大于等于 这个 key 的音问 ID。
  • length,该 Stream 的音问条数。
  • streamID结构体,音问 ID 笼统,集体占 128 位,里面维持了毫秒期间戳(字段 ms);一个毫秒内的自增序号(字段 seq),用于鉴识统一毫秒内插入多条音问。
  • last_id,面前 Stream 终末一条音问的 ID。
  • first_id,面前 Stream 第一条音问的 ID。
  • max_deleted_entry_id,面前 Stream 被苟简的最大的音问 ID。
  • entries_added,通盘有多少条音问补充到 Stream 中,entries_added = 已苟简音问条数 + 未苟简音问条数。
  • *cgroups,rax 指针,也指向一个 Radix Tree ,记载面前 Stream 的集体 Consume Group,每个 Consume Group 的称谓都是独一记号,行径 Radix Tree 的 key,Consumer Group 实例行径 value。

Consumer Group

Consumer Group 由 streamCG 结构体界说,每个 Stream 能够有多个 Consumer Group,一个销耗组能够有多个销耗者同期对组内音问进行销耗。

/* Consumer group. */typedef struct streamCG {     streamID last_id;    long long entries_read;    rax *pel;    rax *consumers;} streamCG;
  • last_id,露出该销耗组的销耗者曾经读捏但还未 ACK 的终末一条音问 ID。
  • *pel,是 pending entries list 简写,指向一个 Radix Tree 的指针,覆灭着 Consumer group 中集体销耗者读捏但还未 ACK 证明的音问,就是这玩意落成了 ACK 机制。该树的 key 是音问 ID,value 联系一个 streamNACK 实例。
  • *consumers, Radix Tree 指针,露出销耗组华厦集体销耗者,key 是销耗者称谓,value 指向一个 streamConsumer 实例。

streamNACK

streamCG -> *pel 对应的 value 是一个 streamNACK 实例,用于笼统销耗者曾经读捏,然而未 ACK 的音问 ID 相故故音问。

/* Pending (yet not acknowledged) message in a consumer group. */typedef struct streamNACK {     mstime_t delivery_time;    uint64_t delivery_count;    streamConsumer *consumer;} streamNACK;
  • delivery_time,该音问终末一次推送给 Consumer 的期间戳。
  • delivery_count,音问被推送次数。
  • *consumer,音问推送的 Consumer 客户端。

streamConsumer

Consumer Group 中对 Consumer 的笼统。

/* A specific consumer in a consumer group.  */typedef struct streamConsumer {     mstime_t seen_time;    sds name;    rax *pel;} streamConsumer;
  • seen_time,销耗者迩来一次被激活的期间戳。
  • name,销耗者称谓。
  • *pel, Radix Tree 指针,马虎统一个音问而言,``streamCG -> pel与streamConsumer -> pel的streamNACK` 实例是统一个。

终末来一张图,便于你领路。

肖材积:“Redis 你好,Stream 若何麇集 Radix Tree 和 listpack 结构来存储音问?为什么不骗捏散列表来存储,音问 ID 行径散列表的 key,散列表的 value 存储音问键值对本色。’”

在答复曾经,先插入几条音问到 Stream,让你对 Stream 音问的存储花色有个大领路知。

该夂箢的语法如下。

XADD key id field value [field value ...]

Stream 华厦每个音问能够贮蓄迥异数量的多个键值对,写入音问获胜后,我会把音问的 ID 返回给客户端。

践诺如下指示把用户进货竹帛的下单音问寄阁阁到 hotlist:books队列,音问本色症结由 payerID、amount 和 orderID。

> XADD hotlist:books * payerID 1 amount 69.00 orderID 91679218539571-0> XADD hotlist:books * payerID 1 amount 36.00 orderID 151679218572182-0> XADD hotlist:books * payerID 2 amount 99.00 orderID 881679218588426-0> XADD hotlist:books * payerID 3 amount 68.00 orderID 801679218604492-0

hotlist:books 是 Stream 的称谓,背面的 “*” 露出让 Redis 为插入的音问自愿生成一个独一 ID,你也能够自界说。

音问 ID 由两部门组成。

  • 面前毫秒内的期间戳。
  • 顺次编号。从 0 为起点值,用于鉴识同临期间内生长的多个夂箢。

肖材积:“若何领路 Stream 是一种只践诺追加独霸(append only)的数据结构?”

通过将元素 ID 与期间进行联系,并逼迫请求新元素的 ID 必须大于旧元素的 ID, Redis 从逻辑上将 Stream 酿成了一种只践诺追加独霸(append only)的数据结构。

用户能够肯定,新的音问和变乱只会呈而今已有音问和变乱之后,就像现实寰宇里新变乱老是爆发在已有变乱之后雷同,齐备都是有序进行的。

肖材积:“插入的音问 ID 大部门雷同,譬喻这四条音问的 ID 都是 1679218 前缀。此外,每条音问键值对的键通常都是雷同的,譬喻这四条音问的键都是 payerID、amount 和 orderID。骗捏散列表存储的话会许多冗尾数据,你这样抠门,是以不骗捏散列表对造故故?”

没缺陷,小老弟很颖慧。为了节约内存,我骗捏了 Radix Tree 和 listpack。Radix Tree 的 key 存储音问 ID,value 骗捏 listpack 数据结构存储多个音问, listapck 华厦音问 ID 都大于等于 key 存储的音问 ID。

我在前面曾经道过 listpack,这是一个紧凑型列表,额外节约内存。而 Radix Tree 数据结构的最大特质是适当覆灭拥有雷同前缀的数据,从而达到节约内存。

终竟 Radix Tree 是若何的数据结构,连缀往下顾念记挂。

Radix Tree

Radix Tree,也被称为 Radix Trie,能够 Compact Prefix Tree),用于高效地存储和查找字符串齐集。它将字符串遵照前缀拆分红一个个字符,并将每个字符行径一个节点存储在树中。

当插入一个键值对时,Redis 会将键遵照字符拆分红一个个字符,并遵照字符在 Radix tree 华厦职位找到适当的节点,要是该节点不糊口,则设立新节点并补充到 Radix tree 中。

当集体字符都补充结束后,将值目的指针覆灭到终末一个节点中。当究诘一个键时,Redis 遵照字符顺次遍历 Radix tree,要是露出某个字符不糊口于树中,则键不糊口;否则,要是终末一个节点露出一个齐备的键,则返回对应的值目的。

如下图露出一个轻省的前缀树,将根节点到叶子节点的阶梯对应字符拼接起来,就赢得了两个 key(“他说碉堡了”、“他说碉炸了”)。

你该当露出了,这两个 key 领有群众前缀(他说碉),前缀树落成了分享骗捏,这样就能够预防雷同字符串重复存储。要是采纳散列表的覆灭花式,阿谁 key 的雷同前缀就会被频频存储,招致内存虚耗。

Radix Tree 革新

每个节点只覆灭一个字符,一是会虚耗内存空间,二是在进行究诘时,还须要逐个结婚每个节点露出的字符,对究诘职能也会酿成训诲。

是以,Redis 并莫得直接骗捏范例前缀树,而是做了一次变种——Compact Prefix Tree(缩短前缀树)。通常来说,当多个 key 拥有雷同的前缀时,那就将雷同前缀的字符阿谀结在一个分享节点中,从而减轻存储空间。

如下几个 key(test、toaster、toasting、slow、slowly)在 Radix Tree 上的结构。

鉴于 Compact Prefix Tree 能够分享雷同前缀的节点,是以在存储一组拥有雷同前缀的键时,Redis 的 Radix tree 比此外数据结构(如哈希表)拥有更低的空间耗尽和更快的究诘速率。

Radix Tree 节点的数据结构由 rax.h文献华厦 raxNode 界说。

typedef struct raxNode {     uint32_t iskey:1;    uint32_t isnull:1;    uint32_t iscompr:1;    uint32_t size:29;    unsigned char data[];} raxNode;
  • iskey:从 Radix Tree 根节点到面前节点组成的字符串是否是一个齐备的 key。是的话 iskey 的值为 1。
  • isnull:面前节点是否为空节点,要是面前节点是空节点的话,就不须要为该节点分配指向 value 的指针内存。
  • iscompr,是否为缩短节点。
  • size,面前节点的大小,险些指会遵照节点规范而迁移。要是是缩短节点,该值露出缩短数据的长度;要是黑白缩短节点,该值露出节点的子节点个数。
  • data[],本质存储的数据,遵照节点规范迥异而有所迥异。
  • 缩短节点,data 数据蕴涵子节点对应的字符、指向子节点的指针,节点为终竟 key 对应的 value 指针。
  • 缩短节点,data 数据贮蓄子节点对应的连续字符串、指向子节点的指针,以及节点为终竟 key 的 value 指针。
  • value 指针指向一个 listpack 实例,里面覆灭了音问本质本色。

Radix Tree 最大的特质就是适当覆灭拥有雷同前缀的数据,落成节约内存的目的,以及赞助限定查找。而这个就是 Stream 采纳 Radix Tree 行径底层数据结构的起因。

责任编辑:姜华 源泉: 码哥字节 Redis音问队列


Redis 据结Stream 数据结构落成道理真的很强

故故者:码哥 数据库 Redis Stream 是 Redis 5.0 版本特意为音问队列设计的数据规范,鉴戒了 Kafka 的构落 Consume Group 设计脉络,提供了销耗组概记挂。成道六一网同期提供了音问的据结耐久化和主从复制机制,客户端能够造访任何期间的构落数据,而且能记着每一个客户端的成道造访职位,从而担保音问不销耗。据结

你好,构落我是成道码哥,一个拥抱硬核身手和目的据结,面向黎民币编程的构落须眉,配阁阁星标不迷途。成道

我在【Redis 据结骗捏 List 落成音问队列的利与弊】说过骗捏 List 落成音问队列有许多限度性。

  • 莫得 ACK 机制。构落
  • 莫得访佛 Kafka 的成道 ConsumerGroup 销耗组概记挂。
  • 音问齐集。
  • List 是线性结构,究诘指定数据须要遍历通盘列表。

1、是什么

Stream 是 Redis 5.0 版本特意为音问队列设计的数据规范,鉴戒了 Kafka 的 Consume Group 设计脉络,提供了销耗组概记挂。

同期提供了音问的耐久化和主从复制机制,客户端能够造访任何期间的数据,而且能记着每一个客户端的造访职位,从而担保音问不销耗。

以下几个是 Stream 规范的症结特质。

  • 骗捏 Radix Tree 和 listpack 结构来存储音问。
  • 音问 ID 序列化生成。
  • 鉴戒 Kafka Consume Group 的六一网概记挂,多个销耗者区分赴任异的 Consume Group 中,销耗统一个 Streams,统一个 Consume Group 的多个销耗者能够通盘并行但不重复销耗,擢升销耗能力。
  • 赞助多播(多对多),阻难和非阻难读捏。
  • ACK 证明机制,担保了音问起码被销耗一次。
  • 可配阁阁音问覆灭上限阈值,我会把汗青音问阵殁,预防内存占用过大。

须要瞩目的是,Redis Stream 是一种超轻量级的 MQ,并莫得无缺落成音问队列的集体设计重点,是以它的骗捏处景须要酌量贸易的数据量和对职能、靠得住性的须要。

适当编制音问量不大,忍受数据销耗,骗捏 Redis Stream 行径音问队列就能纳福高职能连辛苦读写音问的优势

2、修齐心法

每个 Stream 都有一个独一的称谓,行径 Stream 在 Redis 的 key,在首次骗捏 xadd 指示补充音问的韶华会自愿设立。

能够顾念记挂到 Stream 在一个 Redix Tree 树上,树上存储的是音问 ID,每个音问 ID 对应的音问通过一个指针指向 listpack。

Stream 流就像是一个仅追加本色的音问链表,把音问一个个串起来,每个音问都有一个独一的 ID 和音问本色,音问本色则由多个 field/value 键值对组成。底层骗捏 Radix Tree 和 listpack 数据结构存储数据。

为了便于领路,我画了一张图,并对 Radix Tree 的存储数据做了下变形,骗捏列表来阐述 Stream 中音问的逻辑有序性。

这张图触及许多概记挂,然而你不要慌。我一步步拆户口说,终末你再追想顾念记挂就懂了。

先带你屡下集体脉络。

  • Consumer Group:销耗组,每个销耗组能够有一个能够多个销耗者,销耗者之间是较量说合。迥异销耗组的销耗者之间无任何说合。
  • *pel,全称是 Pending Entries List,记载了面前被客户端读捏然而还莫得 ack(Acknowledge character 证明字符)的音问。要是客户端莫得 ack,这个变量的音问 ID 会越来越多。这是一个重点数据结构,用来确保客户端起码销耗音问一次。

Stream 结构

Streams 结构的源码界说在 stream.h 源码华厦 stream 结构体中。

typedef struct stream {     rax *rax;    uint64_t length;    streamID last_id;    streamID first_id;    streamID max_deleted_entry_id;    uint64_t entries_added;    rax *cgroups;} stream;typedef struct streamID {     uint64_t ms;    uint64_t seq;} streamID;
  • *rax,是一个 rax 的指针,指向一个 Radix Tree,key 存储音问 ID,value 本质上指向一个 listpack 数据结构,存储了多条音问,每条音问的 ID 都大于等于 这个 key 的音问 ID。
  • length,该 Stream 的音问条数。
  • streamID结构体,音问 ID 笼统,集体占 128 位,里面维持了毫秒期间戳(字段 ms);一个毫秒内的自增序号(字段 seq),用于鉴识统一毫秒内插入多条音问。
  • last_id,面前 Stream 终末一条音问的 ID。
  • first_id,面前 Stream 第一条音问的 ID。
  • max_deleted_entry_id,面前 Stream 被苟简的最大的音问 ID。
  • entries_added,通盘有多少条音问补充到 Stream 中,entries_added = 已苟简音问条数 + 未苟简音问条数。
  • *cgroups,rax 指针,也指向一个 Radix Tree ,记载面前 Stream 的集体 Consume Group,每个 Consume Group 的称谓都是独一记号,行径 Radix Tree 的 key,Consumer Group 实例行径 value。

Consumer Group

Consumer Group 由 streamCG 结构体界说,每个 Stream 能够有多个 Consumer Group,一个销耗组能够有多个销耗者同期对组内音问进行销耗。

/* Consumer group. */typedef struct streamCG {     streamID last_id;    long long entries_read;    rax *pel;    rax *consumers;} streamCG;
  • last_id,露出该销耗组的销耗者曾经读捏但还未 ACK 的终末一条音问 ID。
  • *pel,是 pending entries list 简写,指向一个 Radix Tree 的指针,覆灭着 Consumer group 中集体销耗者读捏但还未 ACK 证明的音问,就是这玩意落成了 ACK 机制。该树的 key 是音问 ID,value 联系一个 streamNACK 实例。
  • *consumers, Radix Tree 指针,露出销耗组华厦集体销耗者,key 是销耗者称谓,value 指向一个 streamConsumer 实例。

streamNACK

streamCG -> *pel 对应的 value 是一个 streamNACK 实例,用于笼统销耗者曾经读捏,然而未 ACK 的音问 ID 相故故音问。

/* Pending (yet not acknowledged) message in a consumer group. */typedef struct streamNACK {     mstime_t delivery_time;    uint64_t delivery_count;    streamConsumer *consumer;} streamNACK;
  • delivery_time,该音问终末一次推送给 Consumer 的期间戳。
  • delivery_count,音问被推送次数。
  • *consumer,音问推送的 Consumer 客户端。

streamConsumer

Consumer Group 中对 Consumer 的笼统。

/* A specific consumer in a consumer group.  */typedef struct streamConsumer {     mstime_t seen_time;    sds name;    rax *pel;} streamConsumer;
  • seen_time,销耗者迩来一次被激活的期间戳。
  • name,销耗者称谓。
  • *pel, Radix Tree 指针,马虎统一个音问而言,``streamCG -> pel与streamConsumer -> pel的streamNACK` 实例是统一个。

终末来一张图,便于你领路。

肖材积:“Redis 你好,Stream 若何麇集 Radix Tree 和 listpack 结构来存储音问?为什么不骗捏散列表来存储,音问 ID 行径散列表的 key,散列表的 value 存储音问键值对本色。’”

在答复曾经,先插入几条音问到 Stream,让你对 Stream 音问的存储花色有个大领路知。

该夂箢的语法如下。

XADD key id field value [field value ...]

Stream 华厦每个音问能够贮蓄迥异数量的多个键值对,写入音问获胜后,我会把音问的 ID 返回给客户端。

践诺如下指示把用户进货竹帛的下单音问寄阁阁到 hotlist:books队列,音问本色症结由 payerID、amount 和 orderID。

> XADD hotlist:books * payerID 1 amount 69.00 orderID 91679218539571-0> XADD hotlist:books * payerID 1 amount 36.00 orderID 151679218572182-0> XADD hotlist:books * payerID 2 amount 99.00 orderID 881679218588426-0> XADD hotlist:books * payerID 3 amount 68.00 orderID 801679218604492-0

hotlist:books 是 Stream 的称谓,背面的 “*” 露出让 Redis 为插入的音问自愿生成一个独一 ID,你也能够自界说。

音问 ID 由两部门组成。

  • 面前毫秒内的期间戳。
  • 顺次编号。从 0 为起点值,用于鉴识同临期间内生长的多个夂箢。

肖材积:“若何领路 Stream 是一种只践诺追加独霸(append only)的数据结构?”

通过将元素 ID 与期间进行联系,并逼迫请求新元素的 ID 必须大于旧元素的 ID, Redis 从逻辑上将 Stream 酿成了一种只践诺追加独霸(append only)的数据结构。

用户能够肯定,新的音问和变乱只会呈而今已有音问和变乱之后,就像现实寰宇里新变乱老是爆发在已有变乱之后雷同,齐备都是有序进行的。

肖材积:“插入的音问 ID 大部门雷同,譬喻这四条音问的 ID 都是 1679218 前缀。此外,每条音问键值对的键通常都是雷同的,譬喻这四条音问的键都是 payerID、amount 和 orderID。骗捏散列表存储的话会许多冗尾数据,你这样抠门,是以不骗捏散列表对造故故?”

没缺陷,小老弟很颖慧。为了节约内存,我骗捏了 Radix Tree 和 listpack。Radix Tree 的 key 存储音问 ID,value 骗捏 listpack 数据结构存储多个音问, listapck 华厦音问 ID 都大于等于 key 存储的音问 ID。

我在前面曾经道过 listpack,这是一个紧凑型列表,额外节约内存。而 Radix Tree 数据结构的最大特质是适当覆灭拥有雷同前缀的数据,从而达到节约内存。

终竟 Radix Tree 是若何的数据结构,连缀往下顾念记挂。

Radix Tree

Radix Tree,也被称为 Radix Trie,能够 Compact Prefix Tree),用于高效地存储和查找字符串齐集。它将字符串遵照前缀拆分红一个个字符,并将每个字符行径一个节点存储在树中。

当插入一个键值对时,Redis 会将键遵照字符拆分红一个个字符,并遵照字符在 Radix tree 华厦职位找到适当的节点,要是该节点不糊口,则设立新节点并补充到 Radix tree 中。

当集体字符都补充结束后,将值目的指针覆灭到终末一个节点中。当究诘一个键时,Redis 遵照字符顺次遍历 Radix tree,要是露出某个字符不糊口于树中,则键不糊口;否则,要是终末一个节点露出一个齐备的键,则返回对应的值目的。

如下图露出一个轻省的前缀树,将根节点到叶子节点的阶梯对应字符拼接起来,就赢得了两个 key(“他说碉堡了”、“他说碉炸了”)。

你该当露出了,这两个 key 领有群众前缀(他说碉),前缀树落成了分享骗捏,这样就能够预防雷同字符串重复存储。要是采纳散列表的覆灭花式,阿谁 key 的雷同前缀就会被频频存储,招致内存虚耗。

Radix Tree 革新

每个节点只覆灭一个字符,一是会虚耗内存空间,二是在进行究诘时,还须要逐个结婚每个节点露出的字符,对究诘职能也会酿成训诲。

是以,Redis 并莫得直接骗捏范例前缀树,而是做了一次变种——Compact Prefix Tree(缩短前缀树)。通常来说,当多个 key 拥有雷同的前缀时,那就将雷同前缀的字符阿谀结在一个分享节点中,从而减轻存储空间。

如下几个 key(test、toaster、toasting、slow、slowly)在 Radix Tree 上的结构。

鉴于 Compact Prefix Tree 能够分享雷同前缀的节点,是以在存储一组拥有雷同前缀的键时,Redis 的 Radix tree 比此外数据结构(如哈希表)拥有更低的空间耗尽和更快的究诘速率。

Radix Tree 节点的数据结构由 rax.h文献华厦 raxNode 界说。

typedef struct raxNode {     uint32_t iskey:1;    uint32_t isnull:1;    uint32_t iscompr:1;    uint32_t size:29;    unsigned char data[];} raxNode;
  • iskey:从 Radix Tree 根节点到面前节点组成的字符串是否是一个齐备的 key。是的话 iskey 的值为 1。
  • isnull:面前节点是否为空节点,要是面前节点是空节点的话,就不须要为该节点分配指向 value 的指针内存。
  • iscompr,是否为缩短节点。
  • size,面前节点的大小,险些指会遵照节点规范而迁移。要是是缩短节点,该值露出缩短数据的长度;要是黑白缩短节点,该值露出节点的子节点个数。
  • data[],本质存储的数据,遵照节点规范迥异而有所迥异。
  • 缩短节点,data 数据蕴涵子节点对应的字符、指向子节点的指针,节点为终竟 key 对应的 value 指针。
  • 缩短节点,data 数据贮蓄子节点对应的连续字符串、指向子节点的指针,以及节点为终竟 key 的 value 指针。
  • value 指针指向一个 listpack 实例,里面覆灭了音问本质本色。

Radix Tree 最大的特质就是适当覆灭拥有雷同前缀的数据,落成节约内存的目的,以及赞助限定查找。而这个就是 Stream 采纳 Radix Tree 行径底层数据结构的起因。

责任编辑:姜华 源泉: 码哥字节 Redis音问队列


Redis 据结Stream 数据结构落成道理真的很强

故故者:码哥 数据库 Redis Stream 是 Redis 5.0 版本特意为音问队列设计的数据规范,鉴戒了 Kafka 的构落 Consume Group 设计脉络,提供了销耗组概记挂。成道六一网同期提供了音问的据结耐久化和主从复制机制,客户端能够造访任何期间的构落数据,而且能记着每一个客户端的成道造访职位,从而担保音问不销耗。据结

你好,构落我是成道码哥,一个拥抱硬核身手和目的据结,面向黎民币编程的构落须眉,配阁阁星标不迷途。成道

我在【Redis 据结骗捏 List 落成音问队列的利与弊】说过骗捏 List 落成音问队列有许多限度性。

  • 莫得 ACK 机制。构落
  • 莫得访佛 Kafka 的成道 ConsumerGroup 销耗组概记挂。
  • 音问齐集。
  • List 是线性结构,究诘指定数据须要遍历通盘列表。

1、是什么

Stream 是 Redis 5.0 版本特意为音问队列设计的数据规范,鉴戒了 Kafka 的 Consume Group 设计脉络,提供了销耗组概记挂。

同期提供了音问的耐久化和主从复制机制,客户端能够造访任何期间的数据,而且能记着每一个客户端的造访职位,从而担保音问不销耗。

以下几个是 Stream 规范的症结特质。

  • 骗捏 Radix Tree 和 listpack 结构来存储音问。
  • 音问 ID 序列化生成。
  • 鉴戒 Kafka Consume Group 的六一网概记挂,多个销耗者区分赴任异的 Consume Group 中,销耗统一个 Streams,统一个 Consume Group 的多个销耗者能够通盘并行但不重复销耗,擢升销耗能力。
  • 赞助多播(多对多),阻难和非阻难读捏。
  • ACK 证明机制,担保了音问起码被销耗一次。
  • 可配阁阁音问覆灭上限阈值,我会把汗青音问阵殁,预防内存占用过大。

须要瞩目的是,Redis Stream 是一种超轻量级的 MQ,并莫得无缺落成音问队列的集体设计重点,是以它的骗捏处景须要酌量贸易的数据量和对职能、靠得住性的须要。

适当编制音问量不大,忍受数据销耗,骗捏 Redis Stream 行径音问队列就能纳福高职能连辛苦读写音问的优势

2、修齐心法

每个 Stream 都有一个独一的称谓,行径 Stream 在 Redis 的 key,在首次骗捏 xadd 指示补充音问的韶华会自愿设立。

能够顾念记挂到 Stream 在一个 Redix Tree 树上,树上存储的是音问 ID,每个音问 ID 对应的音问通过一个指针指向 listpack。

Stream 流就像是一个仅追加本色的音问链表,把音问一个个串起来,每个音问都有一个独一的 ID 和音问本色,音问本色则由多个 field/value 键值对组成。底层骗捏 Radix Tree 和 listpack 数据结构存储数据。

为了便于领路,我画了一张图,并对 Radix Tree 的存储数据做了下变形,骗捏列表来阐述 Stream 中音问的逻辑有序性。

这张图触及许多概记挂,然而你不要慌。我一步步拆户口说,终末你再追想顾念记挂就懂了。

先带你屡下集体脉络。

  • Consumer Group:销耗组,每个销耗组能够有一个能够多个销耗者,销耗者之间是较量说合。迥异销耗组的销耗者之间无任何说合。
  • *pel,全称是 Pending Entries List,记载了面前被客户端读捏然而还莫得 ack(Acknowledge character 证明字符)的音问。要是客户端莫得 ack,这个变量的音问 ID 会越来越多。这是一个重点数据结构,用来确保客户端起码销耗音问一次。

Stream 结构

Streams 结构的源码界说在 stream.h 源码华厦 stream 结构体中。

typedef struct stream {     rax *rax;    uint64_t length;    streamID last_id;    streamID first_id;    streamID max_deleted_entry_id;    uint64_t entries_added;    rax *cgroups;} stream;typedef struct streamID {     uint64_t ms;    uint64_t seq;} streamID;
  • *rax,是一个 rax 的指针,指向一个 Radix Tree,key 存储音问 ID,value 本质上指向一个 listpack 数据结构,存储了多条音问,每条音问的 ID 都大于等于 这个 key 的音问 ID。
  • length,该 Stream 的音问条数。
  • streamID结构体,音问 ID 笼统,集体占 128 位,里面维持了毫秒期间戳(字段 ms);一个毫秒内的自增序号(字段 seq),用于鉴识统一毫秒内插入多条音问。
  • last_id,面前 Stream 终末一条音问的 ID。
  • first_id,面前 Stream 第一条音问的 ID。
  • max_deleted_entry_id,面前 Stream 被苟简的最大的音问 ID。
  • entries_added,通盘有多少条音问补充到 Stream 中,entries_added = 已苟简音问条数 + 未苟简音问条数。
  • *cgroups,rax 指针,也指向一个 Radix Tree ,记载面前 Stream 的集体 Consume Group,每个 Consume Group 的称谓都是独一记号,行径 Radix Tree 的 key,Consumer Group 实例行径 value。

Consumer Group

Consumer Group 由 streamCG 结构体界说,每个 Stream 能够有多个 Consumer Group,一个销耗组能够有多个销耗者同期对组内音问进行销耗。

/* Consumer group. */typedef struct streamCG {     streamID last_id;    long long entries_read;    rax *pel;    rax *consumers;} streamCG;
  • last_id,露出该销耗组的销耗者曾经读捏但还未 ACK 的终末一条音问 ID。
  • *pel,是 pending entries list 简写,指向一个 Radix Tree 的指针,覆灭着 Consumer group 中集体销耗者读捏但还未 ACK 证明的音问,就是这玩意落成了 ACK 机制。该树的 key 是音问 ID,value 联系一个 streamNACK 实例。
  • *consumers, Radix Tree 指针,露出销耗组华厦集体销耗者,key 是销耗者称谓,value 指向一个 streamConsumer 实例。

streamNACK

streamCG -> *pel 对应的 value 是一个 streamNACK 实例,用于笼统销耗者曾经读捏,然而未 ACK 的音问 ID 相故故音问。

/* Pending (yet not acknowledged) message in a consumer group. */typedef struct streamNACK {     mstime_t delivery_time;    uint64_t delivery_count;    streamConsumer *consumer;} streamNACK;
  • delivery_time,该音问终末一次推送给 Consumer 的期间戳。
  • delivery_count,音问被推送次数。
  • *consumer,音问推送的 Consumer 客户端。

streamConsumer

Consumer Group 中对 Consumer 的笼统。

/* A specific consumer in a consumer group.  */typedef struct streamConsumer {     mstime_t seen_time;    sds name;    rax *pel;} streamConsumer;
  • seen_time,销耗者迩来一次被激活的期间戳。
  • name,销耗者称谓。
  • *pel, Radix Tree 指针,马虎统一个音问而言,``streamCG -> pel与streamConsumer -> pel的streamNACK` 实例是统一个。

终末来一张图,便于你领路。

肖材积:“Redis 你好,Stream 若何麇集 Radix Tree 和 listpack 结构来存储音问?为什么不骗捏散列表来存储,音问 ID 行径散列表的 key,散列表的 value 存储音问键值对本色。’”

在答复曾经,先插入几条音问到 Stream,让你对 Stream 音问的存储花色有个大领路知。

该夂箢的语法如下。

XADD key id field value [field value ...]

Stream 华厦每个音问能够贮蓄迥异数量的多个键值对,写入音问获胜后,我会把音问的 ID 返回给客户端。

践诺如下指示把用户进货竹帛的下单音问寄阁阁到 hotlist:books队列,音问本色症结由 payerID、amount 和 orderID。

> XADD hotlist:books * payerID 1 amount 69.00 orderID 91679218539571-0> XADD hotlist:books * payerID 1 amount 36.00 orderID 151679218572182-0> XADD hotlist:books * payerID 2 amount 99.00 orderID 881679218588426-0> XADD hotlist:books * payerID 3 amount 68.00 orderID 801679218604492-0

hotlist:books 是 Stream 的称谓,背面的 “*” 露出让 Redis 为插入的音问自愿生成一个独一 ID,你也能够自界说。

音问 ID 由两部门组成。

  • 面前毫秒内的期间戳。
  • 顺次编号。从 0 为起点值,用于鉴识同临期间内生长的多个夂箢。

肖材积:“若何领路 Stream 是一种只践诺追加独霸(append only)的数据结构?”

通过将元素 ID 与期间进行联系,并逼迫请求新元素的 ID 必须大于旧元素的 ID, Redis 从逻辑上将 Stream 酿成了一种只践诺追加独霸(append only)的数据结构。

用户能够肯定,新的音问和变乱只会呈而今已有音问和变乱之后,就像现实寰宇里新变乱老是爆发在已有变乱之后雷同,齐备都是有序进行的。

肖材积:“插入的音问 ID 大部门雷同,譬喻这四条音问的 ID 都是 1679218 前缀。此外,每条音问键值对的键通常都是雷同的,譬喻这四条音问的键都是 payerID、amount 和 orderID。骗捏散列表存储的话会许多冗尾数据,你这样抠门,是以不骗捏散列表对造故故?”

没缺陷,小老弟很颖慧。为了节约内存,我骗捏了 Radix Tree 和 listpack。Radix Tree 的 key 存储音问 ID,value 骗捏 listpack 数据结构存储多个音问, listapck 华厦音问 ID 都大于等于 key 存储的音问 ID。

我在前面曾经道过 listpack,这是一个紧凑型列表,额外节约内存。而 Radix Tree 数据结构的最大特质是适当覆灭拥有雷同前缀的数据,从而达到节约内存。

终竟 Radix Tree 是若何的数据结构,连缀往下顾念记挂。

Radix Tree

Radix Tree,也被称为 Radix Trie,能够 Compact Prefix Tree),用于高效地存储和查找字符串齐集。它将字符串遵照前缀拆分红一个个字符,并将每个字符行径一个节点存储在树中。

当插入一个键值对时,Redis 会将键遵照字符拆分红一个个字符,并遵照字符在 Radix tree 华厦职位找到适当的节点,要是该节点不糊口,则设立新节点并补充到 Radix tree 中。

当集体字符都补充结束后,将值目的指针覆灭到终末一个节点中。当究诘一个键时,Redis 遵照字符顺次遍历 Radix tree,要是露出某个字符不糊口于树中,则键不糊口;否则,要是终末一个节点露出一个齐备的键,则返回对应的值目的。

如下图露出一个轻省的前缀树,将根节点到叶子节点的阶梯对应字符拼接起来,就赢得了两个 key(“他说碉堡了”、“他说碉炸了”)。

你该当露出了,这两个 key 领有群众前缀(他说碉),前缀树落成了分享骗捏,这样就能够预防雷同字符串重复存储。要是采纳散列表的覆灭花式,阿谁 key 的雷同前缀就会被频频存储,招致内存虚耗。

Radix Tree 革新

每个节点只覆灭一个字符,一是会虚耗内存空间,二是在进行究诘时,还须要逐个结婚每个节点露出的字符,对究诘职能也会酿成训诲。

是以,Redis 并莫得直接骗捏范例前缀树,而是做了一次变种——Compact Prefix Tree(缩短前缀树)。通常来说,当多个 key 拥有雷同的前缀时,那就将雷同前缀的字符阿谀结在一个分享节点中,从而减轻存储空间。

如下几个 key(test、toaster、toasting、slow、slowly)在 Radix Tree 上的结构。

鉴于 Compact Prefix Tree 能够分享雷同前缀的节点,是以在存储一组拥有雷同前缀的键时,Redis 的 Radix tree 比此外数据结构(如哈希表)拥有更低的空间耗尽和更快的究诘速率。

Radix Tree 节点的数据结构由 rax.h文献华厦 raxNode 界说。

typedef struct raxNode {     uint32_t iskey:1;    uint32_t isnull:1;    uint32_t iscompr:1;    uint32_t size:29;    unsigned char data[];} raxNode;
  • iskey:从 Radix Tree 根节点到面前节点组成的字符串是否是一个齐备的 key。是的话 iskey 的值为 1。
  • isnull:面前节点是否为空节点,要是面前节点是空节点的话,就不须要为该节点分配指向 value 的指针内存。
  • iscompr,是否为缩短节点。
  • size,面前节点的大小,险些指会遵照节点规范而迁移。要是是缩短节点,该值露出缩短数据的长度;要是黑白缩短节点,该值露出节点的子节点个数。
  • data[],本质存储的数据,遵照节点规范迥异而有所迥异。
  • 缩短节点,data 数据蕴涵子节点对应的字符、指向子节点的指针,节点为终竟 key 对应的 value 指针。
  • 缩短节点,data 数据贮蓄子节点对应的连续字符串、指向子节点的指针,以及节点为终竟 key 的 value 指针。
  • value 指针指向一个 listpack 实例,里面覆灭了音问本质本色。

Radix Tree 最大的特质就是适当覆灭拥有雷同前缀的数据,落成节约内存的目的,以及赞助限定查找。而这个就是 Stream 采纳 Radix Tree 行径底层数据结构的起因。

责任编辑:姜华 源泉: 码哥字节 Redis音问队列


Redis 据结Stream 数据结构落成道理真的很强

故故者:码哥 数据库 Redis Stream 是 Redis 5.0 版本特意为音问队列设计的数据规范,鉴戒了 Kafka 的构落 Consume Group 设计脉络,提供了销耗组概记挂。成道六一网同期提供了音问的据结耐久化和主从复制机制,客户端能够造访任何期间的构落数据,而且能记着每一个客户端的成道造访职位,从而担保音问不销耗。据结

你好,构落我是成道码哥,一个拥抱硬核身手和目的据结,面向黎民币编程的构落须眉,配阁阁星标不迷途。成道

我在【Redis 据结骗捏 List 落成音问队列的利与弊】说过骗捏 List 落成音问队列有许多限度性。

  • 莫得 ACK 机制。构落
  • 莫得访佛 Kafka 的成道 ConsumerGroup 销耗组概记挂。
  • 音问齐集。
  • List 是线性结构,究诘指定数据须要遍历通盘列表。

1、是什么

Stream 是 Redis 5.0 版本特意为音问队列设计的数据规范,鉴戒了 Kafka 的 Consume Group 设计脉络,提供了销耗组概记挂。

同期提供了音问的耐久化和主从复制机制,客户端能够造访任何期间的数据,而且能记着每一个客户端的造访职位,从而担保音问不销耗。

以下几个是 Stream 规范的症结特质。

  • 骗捏 Radix Tree 和 listpack 结构来存储音问。
  • 音问 ID 序列化生成。
  • 鉴戒 Kafka Consume Group 的六一网概记挂,多个销耗者区分赴任异的 Consume Group 中,销耗统一个 Streams,统一个 Consume Group 的多个销耗者能够通盘并行但不重复销耗,擢升销耗能力。
  • 赞助多播(多对多),阻难和非阻难读捏。
  • ACK 证明机制,担保了音问起码被销耗一次。
  • 可配阁阁音问覆灭上限阈值,我会把汗青音问阵殁,预防内存占用过大。

须要瞩目的是,Redis Stream 是一种超轻量级的 MQ,并莫得无缺落成音问队列的集体设计重点,是以它的骗捏处景须要酌量贸易的数据量和对职能、靠得住性的须要。

适当编制音问量不大,忍受数据销耗,骗捏 Redis Stream 行径音问队列就能纳福高职能连辛苦读写音问的优势

2、修齐心法

每个 Stream 都有一个独一的称谓,行径 Stream 在 Redis 的 key,在首次骗捏 xadd 指示补充音问的韶华会自愿设立。

能够顾念记挂到 Stream 在一个 Redix Tree 树上,树上存储的是音问 ID,每个音问 ID 对应的音问通过一个指针指向 listpack。

Stream 流就像是一个仅追加本色的音问链表,把音问一个个串起来,每个音问都有一个独一的 ID 和音问本色,音问本色则由多个 field/value 键值对组成。底层骗捏 Radix Tree 和 listpack 数据结构存储数据。

为了便于领路,我画了一张图,并对 Radix Tree 的存储数据做了下变形,骗捏列表来阐述 Stream 中音问的逻辑有序性。

这张图触及许多概记挂,然而你不要慌。我一步步拆户口说,终末你再追想顾念记挂就懂了。

先带你屡下集体脉络。

  • Consumer Group:销耗组,每个销耗组能够有一个能够多个销耗者,销耗者之间是较量说合。迥异销耗组的销耗者之间无任何说合。
  • *pel,全称是 Pending Entries List,记载了面前被客户端读捏然而还莫得 ack(Acknowledge character 证明字符)的音问。要是客户端莫得 ack,这个变量的音问 ID 会越来越多。这是一个重点数据结构,用来确保客户端起码销耗音问一次。

Stream 结构

Streams 结构的源码界说在 stream.h 源码华厦 stream 结构体中。

typedef struct stream {     rax *rax;    uint64_t length;    streamID last_id;    streamID first_id;    streamID max_deleted_entry_id;    uint64_t entries_added;    rax *cgroups;} stream;typedef struct streamID {     uint64_t ms;    uint64_t seq;} streamID;
  • *rax,是一个 rax 的指针,指向一个 Radix Tree,key 存储音问 ID,value 本质上指向一个 listpack 数据结构,存储了多条音问,每条音问的 ID 都大于等于 这个 key 的音问 ID。
  • length,该 Stream 的音问条数。
  • streamID结构体,音问 ID 笼统,集体占 128 位,里面维持了毫秒期间戳(字段 ms);一个毫秒内的自增序号(字段 seq),用于鉴识统一毫秒内插入多条音问。
  • last_id,面前 Stream 终末一条音问的 ID。
  • first_id,面前 Stream 第一条音问的 ID。
  • max_deleted_entry_id,面前 Stream 被苟简的最大的音问 ID。
  • entries_added,通盘有多少条音问补充到 Stream 中,entries_added = 已苟简音问条数 + 未苟简音问条数。
  • *cgroups,rax 指针,也指向一个 Radix Tree ,记载面前 Stream 的集体 Consume Group,每个 Consume Group 的称谓都是独一记号,行径 Radix Tree 的 key,Consumer Group 实例行径 value。

Consumer Group

Consumer Group 由 streamCG 结构体界说,每个 Stream 能够有多个 Consumer Group,一个销耗组能够有多个销耗者同期对组内音问进行销耗。

/* Consumer group. */typedef struct streamCG {     streamID last_id;    long long entries_read;    rax *pel;    rax *consumers;} streamCG;
  • last_id,露出该销耗组的销耗者曾经读捏但还未 ACK 的终末一条音问 ID。
  • *pel,是 pending entries list 简写,指向一个 Radix Tree 的指针,覆灭着 Consumer group 中集体销耗者读捏但还未 ACK 证明的音问,就是这玩意落成了 ACK 机制。该树的 key 是音问 ID,value 联系一个 streamNACK 实例。
  • *consumers, Radix Tree 指针,露出销耗组华厦集体销耗者,key 是销耗者称谓,value 指向一个 streamConsumer 实例。

streamNACK

streamCG -> *pel 对应的 value 是一个 streamNACK 实例,用于笼统销耗者曾经读捏,然而未 ACK 的音问 ID 相故故音问。

/* Pending (yet not acknowledged) message in a consumer group. */typedef struct streamNACK {     mstime_t delivery_time;    uint64_t delivery_count;    streamConsumer *consumer;} streamNACK;
  • delivery_time,该音问终末一次推送给 Consumer 的期间戳。
  • delivery_count,音问被推送次数。
  • *consumer,音问推送的 Consumer 客户端。

streamConsumer

Consumer Group 中对 Consumer 的笼统。

/* A specific consumer in a consumer group.  */typedef struct streamConsumer {     mstime_t seen_time;    sds name;    rax *pel;} streamConsumer;
  • seen_time,销耗者迩来一次被激活的期间戳。
  • name,销耗者称谓。
  • *pel, Radix Tree 指针,马虎统一个音问而言,``streamCG -> pel与streamConsumer -> pel的streamNACK` 实例是统一个。

终末来一张图,便于你领路。

肖材积:“Redis 你好,Stream 若何麇集 Radix Tree 和 listpack 结构来存储音问?为什么不骗捏散列表来存储,音问 ID 行径散列表的 key,散列表的 value 存储音问键值对本色。’”

在答复曾经,先插入几条音问到 Stream,让你对 Stream 音问的存储花色有个大领路知。

该夂箢的语法如下。

XADD key id field value [field value ...]

Stream 华厦每个音问能够贮蓄迥异数量的多个键值对,写入音问获胜后,我会把音问的 ID 返回给客户端。

践诺如下指示把用户进货竹帛的下单音问寄阁阁到 hotlist:books队列,音问本色症结由 payerID、amount 和 orderID。

> XADD hotlist:books * payerID 1 amount 69.00 orderID 91679218539571-0> XADD hotlist:books * payerID 1 amount 36.00 orderID 151679218572182-0> XADD hotlist:books * payerID 2 amount 99.00 orderID 881679218588426-0> XADD hotlist:books * payerID 3 amount 68.00 orderID 801679218604492-0

hotlist:books 是 Stream 的称谓,背面的 “*” 露出让 Redis 为插入的音问自愿生成一个独一 ID,你也能够自界说。

音问 ID 由两部门组成。

  • 面前毫秒内的期间戳。
  • 顺次编号。从 0 为起点值,用于鉴识同临期间内生长的多个夂箢。

肖材积:“若何领路 Stream 是一种只践诺追加独霸(append only)的数据结构?”

通过将元素 ID 与期间进行联系,并逼迫请求新元素的 ID 必须大于旧元素的 ID, Redis 从逻辑上将 Stream 酿成了一种只践诺追加独霸(append only)的数据结构。

用户能够肯定,新的音问和变乱只会呈而今已有音问和变乱之后,就像现实寰宇里新变乱老是爆发在已有变乱之后雷同,齐备都是有序进行的。

肖材积:“插入的音问 ID 大部门雷同,譬喻这四条音问的 ID 都是 1679218 前缀。此外,每条音问键值对的键通常都是雷同的,譬喻这四条音问的键都是 payerID、amount 和 orderID。骗捏散列表存储的话会许多冗尾数据,你这样抠门,是以不骗捏散列表对造故故?”

没缺陷,小老弟很颖慧。为了节约内存,我骗捏了 Radix Tree 和 listpack。Radix Tree 的 key 存储音问 ID,value 骗捏 listpack 数据结构存储多个音问, listapck 华厦音问 ID 都大于等于 key 存储的音问 ID。

我在前面曾经道过 listpack,这是一个紧凑型列表,额外节约内存。而 Radix Tree 数据结构的最大特质是适当覆灭拥有雷同前缀的数据,从而达到节约内存。

终竟 Radix Tree 是若何的数据结构,连缀往下顾念记挂。

Radix Tree

Radix Tree,也被称为 Radix Trie,能够 Compact Prefix Tree),用于高效地存储和查找字符串齐集。它将字符串遵照前缀拆分红一个个字符,并将每个字符行径一个节点存储在树中。

当插入一个键值对时,Redis 会将键遵照字符拆分红一个个字符,并遵照字符在 Radix tree 华厦职位找到适当的节点,要是该节点不糊口,则设立新节点并补充到 Radix tree 中。

当集体字符都补充结束后,将值目的指针覆灭到终末一个节点中。当究诘一个键时,Redis 遵照字符顺次遍历 Radix tree,要是露出某个字符不糊口于树中,则键不糊口;否则,要是终末一个节点露出一个齐备的键,则返回对应的值目的。

如下图露出一个轻省的前缀树,将根节点到叶子节点的阶梯对应字符拼接起来,就赢得了两个 key(“他说碉堡了”、“他说碉炸了”)。

你该当露出了,这两个 key 领有群众前缀(他说碉),前缀树落成了分享骗捏,这样就能够预防雷同字符串重复存储。要是采纳散列表的覆灭花式,阿谁 key 的雷同前缀就会被频频存储,招致内存虚耗。

Radix Tree 革新

每个节点只覆灭一个字符,一是会虚耗内存空间,二是在进行究诘时,还须要逐个结婚每个节点露出的字符,对究诘职能也会酿成训诲。

是以,Redis 并莫得直接骗捏范例前缀树,而是做了一次变种——Compact Prefix Tree(缩短前缀树)。通常来说,当多个 key 拥有雷同的前缀时,那就将雷同前缀的字符阿谀结在一个分享节点中,从而减轻存储空间。

如下几个 key(test、toaster、toasting、slow、slowly)在 Radix Tree 上的结构。

鉴于 Compact Prefix Tree 能够分享雷同前缀的节点,是以在存储一组拥有雷同前缀的键时,Redis 的 Radix tree 比此外数据结构(如哈希表)拥有更低的空间耗尽和更快的究诘速率。

Radix Tree 节点的数据结构由 rax.h文献华厦 raxNode 界说。

typedef struct raxNode {     uint32_t iskey:1;    uint32_t isnull:1;    uint32_t iscompr:1;    uint32_t size:29;    unsigned char data[];} raxNode;
  • iskey:从 Radix Tree 根节点到面前节点组成的字符串是否是一个齐备的 key。是的话 iskey 的值为 1。
  • isnull:面前节点是否为空节点,要是面前节点是空节点的话,就不须要为该节点分配指向 value 的指针内存。
  • iscompr,是否为缩短节点。
  • size,面前节点的大小,险些指会遵照节点规范而迁移。要是是缩短节点,该值露出缩短数据的长度;要是黑白缩短节点,该值露出节点的子节点个数。
  • data[],本质存储的数据,遵照节点规范迥异而有所迥异。
  • 缩短节点,data 数据蕴涵子节点对应的字符、指向子节点的指针,节点为终竟 key 对应的 value 指针。
  • 缩短节点,data 数据贮蓄子节点对应的连续字符串、指向子节点的指针,以及节点为终竟 key 的 value 指针。
  • value 指针指向一个 listpack 实例,里面覆灭了音问本质本色。

Radix Tree 最大的特质就是适当覆灭拥有雷同前缀的数据,落成节约内存的目的,以及赞助限定查找。而这个就是 Stream 采纳 Radix Tree 行径底层数据结构的起因。

责任编辑:姜华 源泉: 码哥字节 Redis音问队列

d1910bdf2a5fe5546f463d4205a42a6.png

Redis Stream 数据结构实现原理真的很强

  • 泰国对中国实施5个月免签政策
  • 预售价109.8万元,仰望U8即将上市
  • 纸杯蛋糕消手游下载
  • 印度拖拉机驾驶模拟器下载
  • 口袋征服怀旧版下载

Redis Stream 数据结构实现原理真的很强

中南财经政法大学

Redis Stream 数据结构实现原理真的很强

湖北省旅游学校

Redis Stream 数据结构实现原理真的很强

湖北专升本学校

Redis Stream 数据结构实现原理真的很强

成功学校

Redis Stream 数据结构实现原理真的很强

武汉江夏城市开发学校

Redis Stream 数据结构实现原理真的很强

湖北技能高考复读班

Redis Stream 数据结构实现原理真的很强

技能高考复读班

Redis Stream 数据结构实现原理真的很强

华中法商专修学院

Redis Stream 数据结构实现原理真的很强

武汉技能高考复读学校

Redis Stream 数据结构实现原理真的很强

武汉技能高考复读班

Redis Stream 数据结构实现原理真的很强

武汉光谷英才学校

Redis Stream 数据结构实现原理真的很强

湖北经济复读培训

Redis Stream 数据结构实现原理真的很强

友情链接