Redis源码解析-基础数据-qulicklist(快速列表)

Redis的快速列表是列表对象list的一种实现,结合了压缩列表和双向链表的优点。它在3.2版本引入,由多个压缩列表节点通过双向链表连接,用以平衡空间和时间效率。快速列表节点的压缩由配置项控制,如`list-max-ziplist-size`和`list-compress-depth`。操作如push、pop和按下标获取元素都有相应的处理策略,其中按下标获取的复杂度为O(N+M)。
摘要由CSDN通过智能技术生成

太长不看版

  • 快速列表是一个元素为压缩列表的双向链表。
  • 快速列表是列表对象list的底层实现之一。
  • 快速列表是在Redis3.2版本引入的。
  • 快速列表节点中压缩列表的最大字节长度(配置项为负数时)或最多元素个数(配置项为正数时)由配置项 list-max-ziplist-size 决定,默认约束为最大长度8Kb。
  • 快速列表提供了选项可以使用LZF压缩算法对中间的节点中的ziplist进行压缩,列表两边多少节点不被压缩由配置项 list-compress-depth 决定,默认对所有节点不进行压缩。

本篇解析基于redis 5.0.0版本,本篇涉及源码文件为quicklist.c, quicklist.h, redic.conf。

什么是快速列表

快速列表是一个元素为压缩列表的双向链表。

qulicklist是列表对象(list)的底层实现之一,是在Redis3.2中为了兼顾空间效率与时间效率而引入的。

压缩列表非常的节省空间,但是当节点过多或者过大之后,因为修改元素时需要重新分配存储空间导致效率会非常低下,所以只在节点较少且较小时使用。
压缩列表相关戳这里了解

双向链表修改节点效率比较高(复杂度O(1)),但是数据结构元数据占用的空间相对比较大(每个节点有两个指针占用16个字节)。

快速列表在时间效率和空间效率之间做了折中,由双向链表将若干个压缩列表连接在一起组成,相比于双向链表更加节省空间,相比于压缩列表操作更加的高效。

快速列表的结构定义

快速列表节点定义

typedef struct quicklistNode {
   
    // 向前指针
    struct quicklistNode *prev;
    // 向后指针
    struct quicklistNode *next;
    // ziplist或者是被压缩之后的ziplist(quicklistLZF)
    unsigned char *zl;
    // ziplist的字节长度,不受压缩影响
    unsigned int sz;
    // ziplist中包含的元素个数
    unsigned int count : 16;     /* count of items in ziplist */
    // ziplist编码标示,1为原始数据,2为被压缩后数据
    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */
    // 数据存储方式(当前只有ziplist)
    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */
    // 需要再次被压缩标记,压缩节点被解压缩读取后会置为1
    unsigned int recompress : 1; /* was this node previous compressed? */
    // 测试用例使用
    unsigned int attempted_compress : 1; /* node can't compress; too small */
    // 预备字段
    unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;

typedef struct quicklistLZF {
   
    // ziplist被压缩之后的字节长度
    unsigned int sz; /* LZF size in bytes*/
    // ziplist被压缩后的内容
    char compressed[];
} quicklistLZF;

typedef struct quicklist {
   
    // 头节点
    quicklistNode *head;
    // 尾节点
    quicklistNode *tail;
    // 快速列表中所有元素的个数
    unsigned long count; 
    // 快速列表节点(quicklistNode)个数
    unsigned long len;
    // 节点中ziplist长度的大小
    int fill : 16; 
    // 头部开始或尾部开始 分别有几个节点不被压缩
    unsigned int compress : 16; 
} quicklist;

因为快速列表的常见的应用场景大多是访问两边的节点(例如:lpush, lpop, rpush, rpop等),为了进一步的节省空间,快速列表提供了选项可以使用LZF压缩算法对中间的节点中的ziplist进行压缩,节点压缩后原本指向ziplist的sz指针元素指向quicklistLZF结构体。
快速列表两边分别有几个节点不被压缩有配置项 list-compress-depth 决定,存储在qulicklist的compress元素中,默认为0(不进行节点压缩)。

快速列表节点中压缩列表的最大字节长度或最多元素个数由配置项 list-max-ziplist-size 决定,存储在qulicklist的fill元素中。配置项为正数时表示节点中压缩列表最多元素个数,为负数时表示节点中压缩列表最大字节长度,默认约束为快速列表节点中压缩列表最大长度为8Kb。

节点ziplist最大长度
-5 64Kb
-4 32Kb
-3 16Kb
-2 8Kb
-1 4Kb

在快速列表的结构体定义中,使用了结构体位域(即 unsigned int count : 16; 这种写法),其实就是为了节省空间某些元素只使用几个bit位长度的空间。详细的解释戳这里

快速列表相关操作

push操作

#ifdef __GNUC__
#  d
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值