Redis quicklist的实现 | 青训营

链表和列表不是一个概念,之后会多次看到这两个词,不要搞混。

Redis 在后来的版本新增设计了两种数据结构:quicklist(Redis 3.2 引入)和 listpack(Redis 5.0 引入)。这两种数据结构的设计目标,就是尽可能地保持压缩列表节省内存的优势,同时解决压缩列表的连锁更新的问题。

这一篇来讲 quicklist 的实现。

quicklist 和压缩列表的区别

其实 quicklist 就是双向链表里存压缩列表,一个 quicklist 就是一个链表,而链表中的每个元素又是一个压缩列表。

quicklist 解决连锁更新的办法:不解决。

quicklist 通过控制每个链表节点中的压缩列表的大小或者元素个数,来规避连锁更新的问题。因为压缩列表元素越少或越小,连锁更新带来的影响就越小,从而提供了更好的访问性能。

结构体定义

quicklist 的结构体跟链表的结构体类似,都包含了表头和表尾,区别在于 quicklist 的节点是 quicklistNode。

c 复制代码
typedef struct quicklist {

    // quicklist 的链表头节点
    quicklistNode *head;
    
    // quicklist 的链表尾节点
    quicklistNode *tail;
    
    // 所有压缩列表中的总元素个数
    unsigned long count;
    
    // quicklistNode 的个数
    unsigned long len;       
    
    ...
    
} quicklist;

接下来看 quicklistNode 的结构定义:

c 复制代码
typedef struct quicklistNode {

    // 前一个 quicklistNode
    struct quicklistNode *prev;
    
    // 后一个 quicklistNode
    struct quicklistNode *next;
    
    // quicklistNode 指向的压缩列表
    unsigned char *zl;              
    
    // 压缩列表的的字节大小
    unsigned int sz;     
    
    // 压缩列表的元素个数
    unsigned int count : 16; 
    
    ...
    
} quicklistNode;

可以看到,quicklistNode 结构体里包含了前一个节点和下一个节点指针,这样每个 quicklistNode 形成了一个双向链表。但是链表节点的元素不再是单纯保存元素值,而是保存了一个压缩列表,quicklistNode 结构体里有个指向压缩列表的指针 *zl

按惯例这里来张图更形象:

在向 quicklist 添加一个元素的时候,不会像普通的链表那样,直接新建一个链表节点。而是会检查插入位置的压缩列表是否能容纳该元素,如果能容纳就直接保存到 quicklistNode 结构里的压缩列表,如果不能容纳,才会新建一个新的 quicklistNode 结构。

简单来说插入是这样的,但是实际使用的时候不会这么简单,因为肯定会有人觉得不能指定插入位置的插入太无聊了,假如想在原来的 quicklist 中间某个节点的压缩列表里的某个位置开始插入,或者某个位置之前插入,问题就有些复杂了。

插入位置的压缩列表还有足够的空位留给插入的数据,那就万事大吉直接放进去。

插入位置的压缩列表空间不够,插入的位置还是这个压缩列表的两端(从压缩列表的头往前插入,或者从压缩列表的尾往后插入),并且压缩列表所在 quicklistNode 两端相邻的 quicklistNode 的压缩列表有足够空位,就插入到这里。

插入位置的压缩列表空间不够,插入位置还是压缩列表的两端,并且压缩列表所在 quicklistNode 两端相邻的 quicklistNode 的压缩列表空间也不够,就只能新建一个 quicklistNode 带上空的压缩列表给这个新插入的值用。

其他情况(主要是插入位置是压缩列表中间),这种就要把原来的压缩列表从插入位置断开,再新建一个 quicklistNode 承载多出来的压缩列表,再往对应位置上插入。

我说的这些情况似乎不包括一个数据就把空的压缩列表撑爆的情况,这种情况要不然就要提前分割数据要不然就不会用到这个数据结构来存。

增删改查的增说完了,删改查简单,这个结构支持的查是根据索引查对应位置上的东西,删的话有可能删区间,区间有可能跨节点,这个因为有压缩列表的大小,也是能定位到从哪到哪都不要直接删的。

quicklist 会控制 quicklistNode 结构里的压缩列表的大小或者元素个数,来规避潜在的连锁更新的风险,但是这并没有完全解决连锁更新的问题。

相关推荐
上进小菜猪2 小时前
基于 YOLOv8 的驾驶员疲劳状态识别系统实战(含完整源码与可视化界面)
后端
上进小菜猪3 小时前
基于 YOLOv8 的交通标识与设施识别系统(含完整源码)
后端
程序员-周李斌3 小时前
Java 死锁
java·开发语言·后端
布列瑟农的星空3 小时前
还在手动翻译国际化词条?AST解析+AI翻译实现一键替换
前端·后端·ai编程
武子康4 小时前
大数据-197 K折交叉验证实战:sklearn 看均值/方差,选更稳的 KNN 超参
大数据·后端·机器学习
码事漫谈4 小时前
C++数据竞争与无锁编程
后端
码事漫谈4 小时前
C++虚函数表与多重继承内存布局深度剖析
后端
weixin_425023005 小时前
Spring Boot + MyBatis Plus JOIN 分页多表查询项目文档
spring boot·后端·mybatis
sxlishaobin5 小时前
Spring Bean生命周期详解
java·后端·spring
肉丸滚球6 小时前
飞算 JavaAI 转 SpringBoot 项目沉浸式体验:高效开发在线图书借阅平台
java·spring boot·后端