Redis List
在Redis3.2版之前,Redis使用压缩列表和双向链表作为List的底层实现。当元素个数比较少并且元素长度比较小时,Redis使用压缩列表实现,否则Redis使用双向链表实现。
ziplist存在问题
不能保存过多的元素,否则查找复杂度高,性能降低。
由于每个节点保存了前一个节点的长度,不同长度使用的字节数不一样,所以在更新节点的时候有可能引起长度的变化导致连锁更新问题。
为了解决上面两个问题,在Redis3.2版之后,引入了quicklist。
quicklist
quicklist可以理解为是ziplist和链表的结合体,一个quicklist是一个双向链表,链表中的每一个节点是一个ziplist。
quicklist结构定义
1 | typedef struct quicklist { |
head:指向头结点的指针
tail:指向尾节点的指针
count:列表中的元素总个数,等于所有节点的ziplist中包含的元素数量之和
len:quicklist中quicklistNode节点的个数
fill:用来限制quicklistNode中ziplist的大小,为正数时代表ziplist中最多能包含的元素个数,为负数时有以下几种情况:
| 数值 | 含义 |
| —- | ——————————- |
| -1 | 表示ziplist的字节数不能超过4KB |
| -2 | 表示ziplist的字节数不能超过8KB |
| -3 | 表示ziplist的字节数不能超过16KB |
| -4 | 表示ziplist的字节数不能超过32KB |
| -5 | 表示ziplist的字节数不能超过64KB |
除此之外,也可以通过list-max-ziplist-size参数配置最大的字节数。
quicklistNode结构定义
1 | typedef struct quicklistNode { |
quicklist创建
1 | quicklist *quicklistCreate(void) { |
添加元素
添加元素的时候可以在链表的头部或者尾部进行添加,以头部添加为例:
- 首先调用_quicklistNodeAllowInsert方法判断是否允许添加元素到ziplist,如果允许,调用ziplistPush方法进行添加
- 如果_quicklistNodeAllowInsert不允许添加元素,则需要新创建一个quicklistNode,然后将元素添加到新创建的quicklistNode的压缩列表中
1 | // 从头部添加元素 |
_quicklistNodeAllowInsert
_quicklistNodeAllowInsert方法用于判断是否允许在某个quicklistNode指向的压缩列表中添加元素。
在quicklist的结构体定义中,fill指定了ziplist中能包含的最大元素个数或者ziplist最大的字节数,_quicklistNodeAllowInsert方法就是判断ziplist中的元素个数或者ziplist的字节数是否超过了限制:
1 | // node:当前的quicklistNode节点 |
总结
在Redis3.2版之前,Redis使用压缩列表和双向链表作为List的底层实现。当元素个数比较少并且元素长度比较小时,Redis使用压缩列表实现,否则Redis使用双向链表实现。
为了解决压缩列表在节点多的时候查找效率低的问题以及连锁更新问题,在Redis3.2版之后引入了quicklist,quicklist是一个双向链表,链表中的每一个节点是一个ziplist。
quicklist中限定了ziplist的大小,如果超过了限制的大小,新加入元素的时候会生成一个新的quicklistNode节点。
quicklist通过限定ziplist的大小来保证一个ziplist中的元素个数不会太多,如果需要连锁更新,也只在某个quicklistNode节点指向的ziplist中更新,不会引发整个链表的更新,以此来解决压缩列表存在的问题。
参考
陈雷《Redis5设计与源码分析》
Redis版本:redis-6.2.5