listpack 设计与实现 listpack 设计与实现 listpack 也叫紧凑列表,它的特点就是用一块连续的内存空间来紧凑地保存数据,同时为了节省内存空间,listpack 列表项使用了多种编码方式 和 listpack 相关的实现文件是listpack.c,头文件包括listpack.h和listpack_malloc.h。 当有新元素插入时,该元素会被插在 listpack 头和尾之间。 好了,了解了 listpack 的整体结构后,我们再来看下 listpack 列表项的设计。 listpack 列表项编码方法我们先来看下 listpack 元素的编码类型。 好,了解了从左向右正向查询 listpack,我们再来看下从右向左反向查询 listpack。
listpack 是什么 紧凑列表,用一块连续的内存空间来紧凑保存数据,同时使用多种编码方式,表示不同长度的数据(字符串、整数)。 结构定义:listpack.h 实现:listpack.c 数据结构 image 编码类型 在 listpack.c 文件中,有大量的 LP_ENCODING__XX_BIT_INT 和 LP_ENCODING 所以在 listpack 中新增或修改元素,只会涉及到列表项自身的操作,不会影响后续列表项的长度变化,进而避免连锁更新。 (lp,LP_HDR_SIZE+1); // 设置 listpack 的元素个数,初始是 0 lpSetNumElements(lp,0); // 设置 listpack 的结尾标识符 /* Return a pointer to the first element of the listpack, or NULL if the * listpack has no elements.
文章目录 listpack ziplist 的级联更新 设计图 PK listpack Stream 定制的数据结构有两个:listpack 和 rax。这篇我们先讲一下 listpack。 listpack 是对 ziplist 的优化。 从5中率先在streams中引入listpack,直到6后作为t_hash御用底层数据结构,redis应该是发现极致的内存使用远远不如提高redis的处理性能。 但是它一旦出现的话就是一场灾难,而 listpack 则是为了解决这一问题而生,所以在这一篇我补上这一个知识点。 级联更新是现有压缩列表构造的一个弊端。 设计图 PK 整体上看,listpack少了一些。其实相比较ziplist,listpack中的优化在于entry中。
基数树用于索引 Listpack,而 Listpack 用于存储 Stream Entry。 但是,如果这个 Listpack 的大小已经达到了预设的上限(默认为 4096 bytes),那么 Redis 就会创建一个新的 Listpack,并将新的 Stream Entry 添加到这个新的 Listpack 所以,基数树和 Listpack 的转换条件主要是 Listpack 的大小是否达到了预设的上限。如果达到了上限,就需要创建新的 Listpack 并更新基数树。 2.2、Listpack Listpack:Listpack 是一种紧凑、高效的列表类型,用于存储多个 Stream Entry。 但是,Listpack 在以下几个方面进行了优化: 更大的最大元素数量:Listpack 可以存储的元素数量比 Ziplist 更多。
Stream 结构 这张图先看个眼熟,Redis Stream 的实现依赖于 Rax 和 listpack,每个消息流都包含一个 Rax 结构,以消息ID为key、listpack节后为value。 .*/ streamID master_id; /* ID of the master entry in the listpack. */ /* Create a new listpack 3、如果该节点已经不能再插入新的消息(listpack为空或已经到达最大存储值),初始化新建的listpack;如果还可以用,则对比插入的消息与listpack中master消息对应的fields内容是否完全一致 4、将待插入的消息内容插入到新建的listpack中或者原来的rax的最后一个key节点对应的listpack中。 只有当一整个listpack都被删除时,才会从rax中释放节点。 裁剪信息流 这是什么意思?举个例子:我只留最近十条信息。就是这个意思。
本文将介绍 Redis 中底层的 listpack(紧凑列表) 的实现方法。 它是 Redis 的 Stream 用到的数据结构之一。 int8 zlend; } listpack 的定义和上方基本一致,只是去掉了 zltail_offset 属性。 让我们回想一下,ziplist 中用这个属性做什么? 新的 listpack 当然是解决了这个问题,才能放心的删除掉这个属性。 listpack 在 5.0 版本引入,但是由于 ziplist 在 Reids 内部的使用太过于广泛,有一些兼容问题,我们可以预见这将是一个逐步的替换过程。 同样在 5.0 版本引入的 Stream 数据结构中,就使用了 listpack 而不是 ziplist.
int in_flags, int *out_flags, double *newscore) { // 重点 1 if (zobj->encoding == OBJ_ENCODING_LISTPACK || sdslen(ele) > server.zset_max_listpack_value || ! 默认 128,还有一个条件是,有序集合保存的所有元素成员的长度都小于 zset_max_listpack_value(64) 字节 我们可以通过 OBJECT ENCODING key 命令查看对象的编码方式 (数据结构) 127.0.0.1:6379> OBJECT ENCODING linkinstar "listpack" 127.0.0.1:6379> OBJECT ENCODING linkinstars if (zobj->encoding == OBJ_ENCODING_LISTPACK) { // 这里只能遍历了 // .... } else if (zobj
最后,注意一个细节 listpack ?是什么? listpack 这个数据结构的设计理念可以参考:https://github.com/antirez/listpack/blob/master/listpack.md /* Each entry in 对于 List 我们能学到: 通过不同的数据结构的组合(双链+listpack)来弥补对应的不足 通过编码数据来优化存储结构和空间(listpack) 这两点是我们能从中学到的,虽然我们不一定会遇到如此要求下的极致优化 参考链接 https://github.com/antirez/listpack/blob/master/listpack.md https://juejin.cn/post/7245854874196459579 https://github.com/zpoint/Redis-Internals/blob/5.0/Object/listpack/listpack.md https://zhuanlan.zhihu.com
」或「压缩表列表」实现,但是在 3.2 版本之后,List 数据类型底层数据结构是由 quicklist 实现的; 在最新的 Redis 代码(还未发布正式版本)中,压缩列表数据结构已经废弃了,交由 listpack 这次,小林把新旧版本的数据结构说图解一遍,共有 9 种数据结构:SDS、双向链表、压缩列表、哈希表、跳表、整数集合、quicklist、listpack。 可以看到,listpack 没有压缩列表中记录前一个节点长度的字段了,listpack 只记录当前节点的长度,当我们向 listpack 加入一个新元素的时候,不会影响其他节点的长度字段的变化,从而避免了压缩列表的连锁更新问题
在 7.0 版本之前是 ziplist,之后被 listpack 代替,使用 listpack 存储的条件是集合元素个数小于等于 zset-max-listpack-entries 配置值(默认 128 ),且 member 占用字节大小小于 zset-max-listpack-value 配置值(默认 64)时使用 listpack 存储,member 和 score 紧凑排列作为 listpack ❝MySQL:“也就是说 listpack 适用于元素个数不多且元素内容不大的场景。” 对,使用 listpack 存储的目的就是节省内存。 listpack ❝MySQL:“根据 zset 结构体定义可知,分别使用了 dict、zskiplist 两种数据结构,listpack 影子都见不着呀。 “ 这个问题问得好,使用 listpack 存储的细节在源码文件t_zset.c 中的zaddGenericCommand函数中体现,部分代码如下,内部会判断是否使用 listpack 来存储。
-2 作用:控制列表(List)类型内部使用 listpack 的最大容量(listpack 是 Redis 7.0 后取代 ziplist 的新结构)。 取值含义:-2:表示每个 listpack 节点最大为 4 KB(默认值)。 正整数(如 4096):表示每个 listpack 节点最大为 指定字节数(单位为字节)。 动态转化逻辑: 当列表元素增多时,Redis 会将列表拆分为多个 listpack 节点组成的 quicklist(双向链表)。 若单个 listpack 节点大小超过此阈值,会自动分裂为两个节点。
在 7.0 版本之前使用 ziplist,之后被 listpack 代替。 field-value pairs 键值对数量小于 hash-max-listpack-entries配置的值(默认 512)。 需要注意的是,不能由 dict 退化成 listpack。 虽然使用了 listpack 就无法实现 O(1) 时间复杂度操作数据,但是使用 listpack 能大大减少内存占用,而且数据量比较小,性能并不是有太大差异。 Hashes 数据类型使用 listpack 作为存储数据时的情况,如图 2-19 所示。
noslowlog-log-slower-than 10000slowlog-max-len 128latency-monitor-threshold 0notify-keyspace-events ""hash-max-listpack-entries 512hash-max-listpack-value 64list-max-listpack-size -2list-compress-depth 0set-max-intset-entries 512zset-max-listpack-entries 128zset-max-listpack-value 64hll-sparse-max-bytes 3000stream-node-max-bytes 4096stream-node-max-entries
返回值:目标集合中的元素个数 内部编码 Redis 的 ZSet(有序集合)是一个功能强大的数据结构,它内部为了在内存使用和操作效率之间取得平衡,根据特定条件,会使用两种不同的内部编码(数据结构):listpack (ziplist在redis7.0+被listpack完全代替)和 skiplist(更准确地说是 skiplist + dict 的组合) listpack(列表包) listpack: ziplist listpack 将长度信息放在每个条目内部,消除了连锁更新。 最多允许的元素个数:zset-max-listpack-entries:128 字符串的最大字节长度:zset-max-listpack-value:64 使用listpack编码时,ZSet所有元素按照 score的值从小到大的顺序,紧密排列在一个listpack中,所以查找元素时需要遍历查找。
使用listpack将使内存更加紧凑。 listpack(redis 7.0+) listpack使用新增配置项: list-max-listpack-size(默认值,每个listpack最大8KB) list-max-listpack-entries (控制 quicklist 中每个 listpack 节点最多能包含的元素数量) quicklist需要的数据太大,这里不好测试,感兴趣的可以自己在redis上测试一下8K的数据(或者使用Lua脚本
内部编码 listpack(7.0+) zipist(压缩列表): 当哈希类型元素个数小于hash-max-ziplist-entries(默认512个)配置同时所有值都小于hash-max-ziplist-value 注意:在redis7.0+版本中出现了重大变化,listpack(列表包)代替了ziplist(压缩列表);listpack有更高的内存效率,更快的追加操作。 特点:Listpack 是 Redis 优化的内存紧凑结构,适合小数据量、内存敏感的场景,解决了 ziplist 的历史问题。 缺点:需要控制哈希在listpack和hashtable两种内部编码的转换,可能会造成内存的较⼤消耗 完结撒花! 如果这篇博客对你有帮助,不妨点个赞支持一下吧!
maxmemory-clients 1g maxmemory-clients 10% 2.4 listpack 紧凑列表 listpack 是用来替代 ziplist 的新数据结构,在 7.0 版本已经没有 redis.config 中对应的配置项: // 下面配置代表 listpack 已经替换了 ziplist 类似 hash-max-ziplist-entries 的配置了。 list-max-listpack-size -2 hash-max-listpack-entries 512 hash-max-listpack-value 64 zset-max-listpack-entries 128 zset-max-listpack-value 64 2.5 传出连接绑定 增加了传出连接(从副本到主服务器,从Sentinel到实例,集群总线等)的绑定配置项,redis.config 中对应的配置项 将 ziplist 替换为 Hash、List、Zset 中的 listpack。 添加对列表类型的支持以存储大于 4GB 的元素 。 重用模块阻塞客户端的临时客户端对象 。
使用 Radix Tree 和 listpack 结构来存储消息。 消息 ID 序列化生成。 可以看到 Stream 在一个 Redix Tree 树上,树上存储的是消息 ID,每个消息 ID 对应的消息通过一个指针指向 listpack。 底层使用 Radix Tree 和 listpack 数据结构存储数据。 图 2-32 图 2-32 ❝肖材积:“Redis 你好,Stream 如何结合 Radix Tree 和 listpack 结构来存储消息? 为了节省内存,我使用了 Radix Tree 和 listpack。
那就是更新后的内部编码了 3.2listpack 定位: Redis 设计的、ziplist 的替代者。 核心设计: 紧凑的连续内存存储。 ListPack 成功继承了 ziplist 的紧凑空间优势并解决了其级联更新的问题。 Redis 巧妙地利用 ListPack 作为小集合的一种极其节省内存的存储格式(牺牲了 O(1) 查找以换取空间),并在数据增长时自动切换到 hashtable 以提供 O(1) 查找性能。 ListPack 的“读取”: ListPack 支持的快速操作是正向或反向的迭代(遍历),得益于其 backlen 的设计(O(1) 跳到下一个或上一个 entry 的起始位置)。 这里说明:listpack是ziplist的优化,但是查找的时间复杂度还是O(N),不是hashtable与ziplist的结合,这里小编前面一篇文章写错了,这里纠正一下 太长的情况下还是:hashtable
listpack(7.0+) Redis 7.0+ 已经将Set的内部编码从 intset 改为 listpack,前面几篇博客都说过,这是redis的重大改变,虽然 listpack 是有序的数据结构 默认配置: inset : 最大元素个数阈值:set-max-intset-entries: 512 packlist 最大元素个数阈值:set-max-listpack-entries: 128 单个元素最大字节数阈值:set-max-listpack-value: 64 注意:一旦从listpack转换为hashtable,就不会再转回listpack,即使删除元素使其小于阈值!