Redis-快速列表(quicklist)
前言: quicklist是一个3.2版本之后新增的基础数据结构,是redis自定义的一种复杂数据结构,
将ziplist和adlist结合到了一个数据结构中
。主要是作为
list的基础数据结构。
在3.2之前,list是根据元素数量的多少采用ziplist或者adlist作为基础数据结构,3.2之后统一改用quicklist
.
定义
从数据结构的角度来说quicklist结合了两种数据结构的优缺点,复杂但是实用。通过将每个压缩表用双向链表的方式连接起来,来寻求一种收益最大化。
特点
quicklist目的是最大程度减小内存空间占用并保证性能
- 链表(adlist)在插入、删除节点的时间复杂度很低,但是
内存利用率低
,且由于内存不连续容易产生内存碎片。利用ziplist减少node数量。 - 压缩表(ziplist)内存连续,存储效率高;但是
插入和删除的成本太高
,需要频繁的进行数据搬移、释放或申请内存。为避免长度较长的ziplist修改时带来的内存拷贝开销,通过配置项配置合理的ziplist长度。
核心数据结构
quicklist
quicklistNode实际上就是对ziplist的进一步封装,quicklist包含多个quicklistNode,quicklistNode由ziplist来保存数据。
注:图示中两端的节点,是没有被压缩的。它们的数据指针zl指向真正的ziplist。中间的其它节点是被压缩过的,它们的数据指针zl指向被压缩后的ziplist结构,即一个quicklistLZF结构。
1 | typedef struct quicklist { |
注:由于quicklist结构包含了压缩表和链表,那么
每个quicklistNode的大小就是一个需要仔细考量的点
。
如果单个quicklistNode存储的数据太多,就会影响插入效率;
如果单个quicklistNode太小,就会变得跟链表一样造成空间浪费。
- quicklist通过属性fill对单个quicklistNode的大小进行限制:fill可以被赋值为正整数或负整数,
当fill值为负数时
,表示单个节点最大的允许的存储空间
:
-1:单个节点最多存储4kb (推荐)
-2:单个节点最多存储8kb (推荐,默认值)
-3:单个节点最多存储16kb (不推荐)
-4:单个节点最多存储32kb (非常不推荐)
-5:单个节点最多存储64kb (通常情况下不要设置这个值)当fill值为正数时
,表示单个节点最大允许的元素个数
,最大为32768个
注:通常在-2或-1时, 性能表现最好在redis内部使用中,默认的最大单节点数据量设置是-2,也就是8kb;
quicklistNode
1 | typedef struct quicklistNode { |
注:这里从变量count开始,都采用了位域的方式进行数据的内存声明,使得6个unsigned int变量只用到了一个unsigned int的内存大小。
插入节点过程
如果你插入一个新的元素到 quicklist 中
- 如果当前的 quicklist 是空的, 创建一个新的 ziplist 并且把这个元素插入到 ziplist 中;
- 如果当前的 quicklist 不是空的, 如果这个要插入的节点还有位置(count < fill), 找到你想要插入的位置,插入到当前节点的 ziplist 中;
- 如果没位置了, 但是你插入的是 quicklist 的头部或者尾部(lpush/rpush), 创建一个新的 ziplist 并进行插入
- 不然的话(没位置并且插入中间), 把中间的 ziplist 按照你插入的位置为界, 分成两个 ziplist, 插入并连接起来
删除节点过程
在 redis/src/quicklist.c 中定义了如下函数原型 int quicklistDelRange(quicklist *quicklist, const long start, const long count), 这个函数会遍历所有的节点, 知道你指定的范围都删除为止。
示例1
- 我在配置文件做了如下两行的更改
1 | list-max-ziplist-size 3 |
- 插入首个节点
1 | 127.0.0.1:6379> lpush lst 123 456 |
1 | 127.0.0.1:6379> lpush lst 789 |
示例2
- 我在配置文件做了如下两行的更改
1 | list-max-ziplist-size 3 |
- 插入节点
1 | 127.0.0.1:6379> del lst |
1 | 127.0.0.1:6379> linsert lst after bb2 123 |
Redis配置
list-max-ziplist-size
Redis的配置文件中,给出了每个ziplist中长度的设定,可通过配置项配置合理的ziplist长度,设置根据场景而定。对应quicklist的fill属性,取值含义同fill属性值。例如
1 | list-max-ziplist-size -2 |
list-compress-depth
因为链表的特性,一般首尾两端操作较频繁,中部操作相对较少,所以redis提供压缩深度配置
:
参数list-compress-depth的取值含义如下:
0: 是个特殊值,表示都不压缩。这是Redis的默认值。
1: 表示quicklist两端各有1个节点不压缩,中间的节点压缩。
2: 表示quicklist两端各有2个节点不压缩,中间的节点压缩。
3: 表示quicklist两端各有3个节点不压缩,中间的节点压缩。
依此类推,最大16bit
小结
- 快速列表将压缩表和链表结合到了一个数据结构中`。主要是作为list的基础数据结构;
- 可通过配置项
list-max-ziplist-size
设定合理的ziplist大小;值为负代表从字节大小限定,值为正代表元素个数限定; - 可通过配置项
list-compress-depth
设定压缩深度配置;
参考:
https://github.com/zpoint/Redis-Internals/blob/5.0/Object/list/list.md > http://czrzchao.com/redisSourceQuicklist#quicklist > https://blog.csdn.net/qq_31720329/article/details/99938219