![v2-05e7281bc197fa759afed75dbd42f71e_1440w.jpg?source=172ae18b](http://img-01.proxy.5ce.com/view/image?&type=2&guid=f0e1d228-7e2f-eb11-8da9-e4434bdf6706&url=https://pic1.zhimg.com/v2-05e7281bc197fa759afed75dbd42f71e_1440w.jpg?source=172ae18b)
- 前言
- 定义
- 优劣
- ziplist 切割大小
- 压缩深度
- 总结
- 参考文章
- 联系我
前言
Redis 已经是大家耳熟能详的东西了,日常工作也都在使用,面试中也是高频的会涉及到,那么我们对它究竟了解有多深刻呢?
我读了几本 Redis 相关的书籍,尝试去了解它的具体实现,将一些底层的数据结构及实现原理记录下来。
本文将介绍 Redis 中底层的 quicklist(快速列表) 的实现方法。 它是 Redis 中列表键的底层实现之一。
![v2-86a89ba70d43c4df892830230e31e52f_b.png](http://img-03.proxy.5ce.com/view/image?&type=2&guid=f0e1d228-7e2f-eb11-8da9-e4434bdf6706&url=https://pic4.zhimg.com/v2-86a89ba70d43c4df892830230e31e52f_b.png)
可以看到图中,这个键值为listkey
的 list 内部使用的编码方法就是 quicklist.
定义
quicklist 是 ziplist 和 linkedlist 的一个结合体。它的结构定义如下:
struct ziplist_compressed{
int32 size;
byte[] compressed_data;
}
struct quicklistNode {
quicklistNode* prev;
quicklistNode* next;
// 指向压缩列表
ziplist* zi;
// ziplist 的字节总数
int32 size;
// ziplist 的元素总数
int 16 count;
// 存储形式,是原生的字节数组,还是 LZF 压缩存储
int2 encoding;
}
struct quicklist{
// 头结点
quicklistNode* head;
// 尾节点
quicklistNode* tail;
// 元素总数
long count;
// ziplist 节点的个数
int nodes;
// LZF 算法压缩深度
int compressDepth;
}
从结构定义中可以看到,quicklist 的定义和 链表的很像,本质上也是一个双端的链表,只是把普通的节点换成了 quicklistNode, 在这个节点中,保存的不是一个简单的值,而是一个 ziplist.
优劣
为什么要定义一个 quicklist, 列表结构还不够多吗???
纯粹的使用 Linkedlist, 也就是普通链表来存储数据有两个弊端:
- 每个节点都有自己的前后指针,指针所占用的内存有点多,太浪费了。
- 每个节点单独的进行内存分配,当节点过多,造成的内存碎片太多了。影响内存管理的效率。
因此,定义了 quicklist, 将 linkedlist 和 ziplist 结合起来,形成一个,将多个 ziplist 通过前后指针互相连接起来的结构,可以在一定程度上缓解上面说到的两个问题。
为了进一步节约内存,Reids 还可以对 ziplist 进行压缩存储,应用 LZF 算法压缩。
ziplist 切割大小
既然 quicklist 本质上是将 ziplist 连接起来,那么每个 ziplist 存放多少的元素,就成为了一个问题。
太小的话起不到应有的作用,极致小的话(为 1 个元素), 快速列表就退化成了普通的链表。
太大的话性能太差,极致大的话(整个快速列表只用一个 ziplist), 快速列表就退化成了 ziplist.
quickli 内部默认定义的单个 ziplist 的大小为 8k 字节
. 超过这个大小,就会重新分配一个 ziplist 了。这个长度可以由参数list-max-ziplist-size
来控制。
压缩深度
前面提到了,quicklist 可以对 ziplist 来进行压缩,而且可以指定压缩深度。(由list-compress-depth
参数决定).
默认的压缩深度为 0, 也就是所有的节点都不压缩。
为了支持快速的 push/pop 操作,quicklist 两端的第一个 ziplist 不进行压缩,这时压缩深度为 1.
如果压缩深度为 2, 则是两端各自两个 ziplist 不压缩。
因为如果将一个 ziplist 压缩,那么要从它里面读取值,必然要先解压,会造成性能变差,因此可以将两端即将被操作的节点不压缩,其他的选择压缩。
总结
总结,为了解决 linkedlist 的双向指针占用内存过多,以及 ziplist 数据量太大性能就变差的问题,结合他们两个产出了新的数据结构,也就是 quicklist
.
它将多个 ziplist 通过前后节点的指针连接起来,在一定程度上解决了上面的问题,提高了 Redis 的响应速度。
参考文章
《Redis 的设计与实现(第二版)》
《Redis 深度历险:核心原理和应用实践》
https://matt.sh/redis-quicklist
完。
联系我
最后,欢迎关注我的个人公众号【 呼延十 】,会不定期更新很多后端工程师的学习笔记。 也欢迎直接公众号私信或者邮箱联系我,一定知无不言,言无不尽。
以上皆为个人所思所得,如有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文链接。
联系邮箱:huyanshi2580@gmail.com
更多学习笔记见个人博客或关注微信公众号 < 呼延十 >------>呼延十