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

相关推荐
WanderInk1 分钟前
JavaWeb CRUD 与分页系统架构学习教程
java·后端·架构
有一只柴犬2 分钟前
深度剖析:Spring Event 机制的源码之旅
后端
peixinwang2 分钟前
微服务架构设计模式(Chris Richardson)阅读笔记(一)
后端
慢慢即慢慢5 分钟前
关于物联网个人技术汇总
后端
YMWM11 分钟前
python2中多进程库multiprocessing.pool.ThreadPool.Pool使用
后端
君秋水12 分钟前
FastAPI教程:20个核心概念从入门到 happy使用
后端·python·程序员
凌览22 分钟前
2.3k Star!免费又好用的图片压缩神器,1 秒瘦身不模糊!
前端·后端·面试
天上掉下来个程小白23 分钟前
开发环境搭建-06.后端环境搭建-前后端联调-Nginx反向代理和负载均衡概念
java·运维·spring boot·后端·nginx·负载均衡·苍穹外卖
顽石九变38 分钟前
【SpringBoo3】SpringBoot项目Web拦截器使用
spring boot·后端
梦兮林夕1 小时前
从零掌握 Gin 参数解析与验证
后端·go·gin