Redis 快速链表相关源码阅读笔记,源码文件 quicklist.h & quicklist.c。
1. 小结
quicklist 是由 ziplist 组成的双向链表,兼顾节省内存空间和提高查询效率的需求。
- 每个
quicklistNode中包含一个ziplist,此ziplist的长度受到 list-max-ziplist-size 的限制 - 考虑到链表的读写操作通常集中于两端,故
quicklist中间部分的quicklistNode中的ziplist可能会使用 LZF 算法进行压缩以进一步节省内存空间,压缩程度由 list-compress-depth 控制
2. quicklist 相关数据结构
quicklist 结构定义如下:
|
|
-
quicklistNode: 快速链表节点,包含一ziplist,其结构介绍将在下文详述 -
count: 该快速链表中所有ziplist的zlentry总数 -
len: 该快速链表中quicklistNode个数 -
fill: 其用于限制该快速链表中每个quicklistNode中ziplist的长度- 从 ziplist 那一章节中我们知道
ziplist每次更改操作耗时和其长度成正比,因此ziplist不应过长,但是如果其过短的话就无法达到节省内存空间的效果,特别地,当fill = 1时,quicklist退化成普通的双向链表 - 可在
redis.conf中通过设置list-max-ziplist-size进行配置-
其值为正数时,表示的是每个
ziplist中zlentry个数(zllen)上限 -
其值为负数时,表示的是每个
ziplist所用字节总数(zlbytes)上限1 2 3 4 5-5: max size: 64 Kb <-- not recommended for normal workloads -4: max size: 32 Kb <-- not recommended -3: max size: 16 Kb <-- probably not recommended -2: max size: 8 Kb <-- good -1: max size: 4 Kb <-- good
-
- 从 ziplist 那一章节中我们知道
-
compress: 链表的读写通常情况下集中在两端,因此在quicklist中,可能会对中间的quicklistNode节点使用无损数据压缩算法 LZF 对其包含的ziplist进行压缩以进一步节省内存使用。compress的大小含义如下:- $0$: 表示不进行压缩
- $\mathbb{Z}^+$: 表示首尾
compress个quicklistNode不会被压缩
- 可以看出,
head和tail节点永远不会被压缩
quicklistBookmark
quicklist 中最后一个 field 为一个柔性数组成员 bookmarks,bookmark_count 保存该数组的长度。当 quicklist 长度特别长,需要迭代遍历时,会使用到该数组作为缓存。该数组的长度应保持在较小值,以供高效查找更新,其结构定义如下:
|
|
quicklistNode
quicklistNode 包装了单个 ziplist,其结构定义如下:
|
|
-
sz:ziplist所占用的字节总数(zlbytes) -
count: 16-bits(0 to 65535),表示在ziplist中zlentry个数(zllen)。- 我们知道
ziplist的长度受到quicklist中fill参数的限制,在 32 位机器上QL_FILL_BITS = 14,在 64 位机器上QL_FILL_BITS = 16- 当
fill > 0时,表示zllen,其最大值为 32767,小于 65535 - 当
fill < 0时,表示zlbytes,其最大值为 64kb,从 ziplist 中我们知道,当所有zlentry存储的是 0 到 12 的整数时,每个zlentry的大小为 $sizeof(prevlen) + sizeof(encoding) = 1 + 1 = 2$ 字节,$64 \times 1024 \div 2 = 32768 < 65535$
- 当
- 综上可知,16-bit 的
count足够使用
- 我们知道
-
encoding: 表示所包含的ziplist是否被压缩,值为 1 表示未被压缩,值为 2 表示已使用 LZF 压缩算法压缩 -
container: 表示quicklistNode所包装的数据类型,目前值固定为 2,表示包装的数据类型为ziplist -
recompress: 当我们访问ziplist时,需要解压数据,该参数表示ziplist是否被临时解压 -
attempted_compress: 测试用
quicklistLZF
当 quicklistNode 中的 ziplist 被压缩后,quicklistNode->zl 指向 quicklistLZF 结构,其定义如下:
|
|
值得注意的是,此处 sz 指的是压缩后得到的 compressed 字节数组的长度,而 quicklistNode->sz 指的是 ziplist 压缩前的长度。
quicklistIter
quicklist 的迭代器,其结构如下:
|
|
quicklist: 当前迭代的快速链表current: 当前迭代的quicklistNodezi: 当前迭代的zlentryoffset: 当前访问的zlentry在current->zl中的偏移量direction: 迭代方向
quicklistEntry
用于描述 quicklist 中的每个 zlentry 的状态。
|
|
quicklist: 指向该zlentry所在的快速链表node: 指向该zlentry所在的quicklistNodezi: 指向该zlentryoffset: 该zlentry在node->zl中的偏移量- 当
zlentry的数据类型为字符串时,value和sz保存了zlentry的值 - 当
zlentry的数据类型为整型时,longval保存了zlentry的值