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 结构里的压缩列表的大小或者元素个数,来规避潜在的连锁更新的风险,但是这并没有完全解决连锁更新的问题。

相关推荐
candyTong18 分钟前
Claude Code 的 Edit 工具是怎么工作的
javascript·后端·架构
GetcharZp1 小时前
GitHub 2.4 万 Star!D2 正在重新定义程序员画图方式
后端
zhangxingchao3 小时前
多 Agent 架构到底怎么选?从 Claude Agent Teams、Cognition/Devin 到工程落地原则
前端·人工智能·后端
IT_陈寒3 小时前
SpringBoot那个自动配置的坑,害我排查到凌晨三点
前端·人工智能·后端
ServBay3 小时前
OpenCode 和它的7款必备插件
后端·github·ai编程
ping某3 小时前
逐字节拆解 tcpdump
后端
阿凡9807303 小时前
花 100 dollar,用 Claude 打通 EasyEDA&Fusion 双向同步
后端·程序员
irving同学462383 小时前
从零搭建生产级 RAG:Embedding、Chunking、Hybrid Search 与 Reranker
前端·后端
她的男孩4 小时前
从零搭一个企业后台,为什么我把能力拆成 Starter 和 Plugin
java·后端·架构
胡志辉4 小时前
本地 AI 编码助手从 0 配起来:先选模型,再接 Ollama、VS Code、Claude Code 和 Codex
前端·后端