前言
Redis 已经是大家耳熟能详的东西了,日常工作也都在使用,面试中也是高频的会涉及到,那么我们对它究竟了解有多深刻呢?
我读了几本 Redis 相关的书籍,尝试去了解它的具体实现,将一些底层的数据结构及实现原理记录下来。
本文将介绍 Redis 中底层的 listpack(紧凑列表) 的实现方法。 它是 Redis 的 Stream 用到的数据结构之一。
定义
Redis 设计 listpack 的目的就是取代 ziplist, 在 Redis 系列(三)底层数据结构之压缩列表 中我们提到,ziplist 在极小的概率下有可能发生级联更新,当连续规模较大的级联更新发生时,对 Redis 的性能有比较大的影响。
虽然我们都知道这是极小的概率,但是这种设计缺陷却不能被 Redis 的大佬作者所接受,因此在 5.0 版本中新引入了一个数据结构,名叫 listpack, 大家都将它翻译为 紧凑列表.
它的定义和 ziplist 极其相似,只是通过一些新的设计,来解决 ziplist 中的痛点问题。因此本文的讲解基于读者已经了解 ziplist.
ziplist的定义如下:
注意:这是 ziplist 的定义
struct ziplist<T>{
// 整个压缩列表占用字节数
int32 zlbytes;
// 最后一个节点到压缩列表起始位置的偏移量,可以用来快速的定位到压缩列表中的最后一个元素
int32 zltail_offset;
// 压缩列表包含的元素个数
int16 zllength;
// 元素内容列表,用数组存储,内存上紧挨着
T[] entries;
// 压缩列表的结束标志位,值永远为 0xFF.
int8 zlend;
}
listpack 的定义和上方基本一致,只是去掉了 zltail_offset 属性。
让我们回想一下,ziplist 中用这个属性做什么?用来方便的找到最后一个节点,然后方便进行反向的遍历。新的 listpack 当然是解决了这个问题,才能放心的删除掉这个属性。
listpack节点的定义如下:
int<var> encoding;
optional byte[] content;
int<var> length;
相比于 ziplist 的定义,它有两点改动:
- 记录的长度不再是前一个节点的长度,而是自己的长度。
- 将记录自己的长度放到了节点的尾部。
这样做的好处是:
- 不再需要 zltail_offset 属性也可以快速定位到最后一个节点。用
listpac 的总长度-最后一个节点的长度
. - 每个节点记录自己的长度,当本节点的值发生了改变,只需要更改自己的长度即可。不再需要更改别的节点的属性,也就彻底的解决掉了级联更新问题。
总结
listpack 是 Redis 设计用来取代掉 ziplist 的数据结构,它通过每个节点记录自己的长度,且放在节点的尾部,来彻底解决掉了 ziplist 存在的级联更新的问题。
listpack 在 5.0 版本引入,但是由于 ziplist 在 Reids 内部的使用太过于广泛,有一些兼容问题,我们可以预见这将是一个逐步的替换过程。
同样在 5.0 版本引入的 Stream 数据结构中,就使用了 listpack 而不是 ziplist.
参考文章
《Redis 的设计与实现(第二版)》
《Redis 深度历险:核心原理和应用实践》
完。
联系我
最后,欢迎关注我的个人公众号【 呼延十 】,会不定期更新很多后端工程师的学习笔记。
也欢迎直接公众号私信或者邮箱联系我,一定知无不言,言无不尽。
以上皆为个人所思所得,如有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文链接。
联系邮箱:huyanshi2580@gmail.com
更多学习笔记见个人博客或关注微信公众号 < 呼延十 >------>呼延十