前言

Redis 已经是大家耳熟能详的东西了,日常工作也都在使用,面试中也是高频的会涉及到,那么我们对它究竟了解有多深刻呢?

我读了几本 Redis 相关的书籍,尝试去了解它的具体实现,将一些底层的数据结构及实现原理记录下来。

本文将介绍 Redis 中底层的 quicklist(快速列表) 的实现方法。 它是 Redis 中列表键的底层实现之一。

2020-01-05-16-56-43

可以看到图中,这个键值为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, 也就是普通链表来存储数据有两个弊端:

  1. 每个节点都有自己的前后指针,指针所占用的内存有点多,太浪费了。
  2. 每个节点单独的进行内存分配,当节点过多,造成的内存碎片太多了。影响内存管理的效率。

因此,定义了 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

更多学习笔记见个人博客或关注微信公众号 < 呼延十 >------>呼延十


Doto丶
151 声望6 粉丝

不知名后端工程师,喜欢code,喜欢学习,喜欢记录.