Redis系列(六):数据结构QuickList(快速列表)源码解析
1.介绍
Redis在3.2版本之前List的底层编码是ZipList和LinkedList实现的
在3.2版本之后,重新引入了QuickList的数据结构,列表的底层都是QuickList实现
当List对象中元素的长度比较小或者数量比较少的时候,采用ZipList来存储
当List对象中元素的长度比较大或者数量比较多的时候,采用LinkList来存储
这两种存储方式的优缺点
LinkedList便于在表的两端进行Push和Pop操作,在插入节点复杂度很低,但是它的内存开销很大,首先,它在每个节点上除了要保存数据之外,还要额外保存两个指针。
其次;双向链表的各个节点时单独的内存块,地址不连续,节点多了容易产生内存碎片
ZipList存储在一段连续的内存上,所以存储效率很高,但是它不利于修改操作,插入和删除操作需要频繁的申请释放内存。
特别是当ZipList长度很长的时候,一次Realloc可能导致大批量的数据拷贝
QuickList可以看作一个双向列表,但是列表的每个节点都是一个ziplist,
其实就是linkedlist和ziplist的结合。quicklist中的每个节点ziplist都能够存储多个数据元素。
2.示意图

3.源码实现
typedef struct quicklist {
quicklistNode *head; // 指向quicklist的头部
quicklistNode *tail; // 指向quicklist的尾部
unsigned long count; // 列表中所有数据项的个数总和
unsigned int len; // quicklist节点的个数,即ziplist的个数
int fill : ; // ziplist大小限定,由list-max-ziplist-size给定
unsigned int compress : ; // 节点压缩深度设置,由list-compress-depth给定
} quicklist;
count用来统计所有数据项的个数总和,len用来统计quicklist的节点个数, 因为每个节点ziplist都能存储多个数据项,所以有了这两个统计值。
typedef struct quicklistNode {
struct quicklistNode *prev; // 指向上一个ziplist节点
struct quicklistNode *next; // 指向下一个ziplist节点
unsigned char *zl; // 数据指针,如果没有被压缩,就指向ziplist结构,反之指向quicklistLZF结构
unsigned int sz; // 表示指向ziplist结构的总长度(内存占用长度)
unsigned int count : ; // 表示ziplist中的数据项个数
unsigned int encoding : ; // 编码方式,1--ziplist,2--quicklistLZF
unsigned int container : ; // 预留字段,存放数据的方式,1--NONE,2--ziplist
unsigned int recompress : ; // 解压标记,当查看一个被压缩的数据时,需要暂时解压,标记此参数为1,之后再重新进行压缩
unsigned int attempted_compress : ; // 测试相关
unsigned int extra : ; // 扩展字段,暂时没用
} quicklistNode;
QuickList的迭代器结构,指向迭代器节点元素
// quicklist的迭代器结构
typedef struct quicklistIter {
const quicklist *quicklist; // 指向所在quicklist的指针
quicklistNode *current; // 指向当前节点的指针
unsigned char *zi; // 指向当前节点的ziplist
long offset; // 当前ziplist中的偏移地址
int direction; // 迭代器的方向
} quicklistIter;
// 表示quicklist节点中ziplist里的一个节点结构
typedef struct quicklistEntry {
const quicklist *quicklist; // 指向所在quicklist的指针
quicklistNode *node; // 指向当前节点的指针
unsigned char *zi; // 指向当前节点的ziplist
unsigned char *value; // 当前指向的ziplist中的节点的字符串值
long long longval; // 当前指向的ziplist中的节点的整型值
unsigned int sz; // 当前指向的ziplist中的节点的字节大小
int offset; // 当前指向的ziplist中的节点相对于ziplist的偏移量
} quicklistEntry;
创建QuickList
fill:
-5: 每个quicklist节点上的ziplist大小不能超过64 Kb。(注:1kb => 1024 bytes)
-4: 每个quicklist节点上的ziplist大小不能超过32 Kb。
-3: 每个quicklist节点上的ziplist大小不能超过16 Kb。
-2: 每个quicklist节点上的ziplist大小不能超过8 Kb。(-2是Redis给出的默认值)
-1: 每个quicklist节点上的ziplist大小不能超过4 Kb。
compress:
0 特殊值,表示不压缩
1 表示quicklist两端各有一个节点不压缩,中间的节点压缩
2 表示quicklist两端各有两个节点不压缩,中间的节点压缩
3 表示quicklist两端各有三个节点不压缩,中间的节点压缩
quicklist *quicklistCreate(void) {
struct quicklist *quicklist; // 声明指针
quicklist = zmalloc(sizeof(*quicklist)); // 分配内存
quicklist->head = quicklist->tail = NULL; // 设定头尾指针
quicklist->len = ; // 设定长度
quicklist->count = ; // 设定数据项总和
quicklist->compress = ; // 设定压缩深度
quicklist->fill = -; // 设定ziplist大小限定
return quicklist;
}
创建QuickListNode
REDIS_STATIC quicklistNode *quicklistCreateNode(void) {
quicklistNode *node;
node = zmalloc(sizeof(*node)); // 申请内存
node->zl = NULL; // 初始化指向ziplist的指针
node->count = ; // 初始化数据项个数
node->sz = ; // 初始化ziplist大小
node->next = node->prev = NULL; // 初始化prev和next指针
node->encoding = QUICKLIST_NODE_ENCODING_RAW; // 初始化节点编码方式
node->container = QUICKLIST_NODE_CONTAINER_ZIPLIST; // 初始化存放数据的方式
node->recompress = ; // 初始化再压缩标记
return node;
}
Redis系列(六):数据结构QuickList(快速列表)源码解析的更多相关文章
- Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
概要 前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...
- Java 集合系列07之 Stack详细介绍(源码解析)和使用示例
概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...
- Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例
概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...
- Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例
概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...
- 【转】Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例
概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...
- 【转】 Java 集合系列07之 Stack详细介绍(源码解析)和使用示例
概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...
- Spring Cloud系列(四):Eureka源码解析之客户端
一.自动装配 1.根据自动装配原理(详见:Spring Boot系列(二):Spring Boot自动装配原理解析),找到spring-cloud-netflix-eureka-client.jar的 ...
- 设置ZooKeeper服务器地址列表源码解析及扩展
设置ZooKeeper服务器地址列表源码解析及扩展 ZooKeeper zooKeeper = new ZooKeeper("192.168.109.130:2181",SESSI ...
- Spring系列(六):Spring事务源码解析
一.事务概述 1.1 什么是事务 事务是一组原子性的SQL查询,或者说是一个独立的工作单元.要么全部执行,要么全部不执行. 1.2 事务的特性(ACID) ①原子性(atomicity) 一个事务必须 ...
- 【转】Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
概要 前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...
随机推荐
- 解决IIS应用程序池默认回收导致程序崩溃
这些网上常见的解决: 其实他们只知其一不知其二:一味的调长超时时间根本就是治标不治本的解决方案, 超时时间再长也会出现到期时间,那时候我们该怎么办呢?(某些吃瓜群众就会大喊:那我就手动去重启一下呗 ...
- 利用init进程监控底层节点的方法架构
native层利用底层节点变化,再针对变化进行相应的函数调用,实现某些功能. 架构如下: 底层提供节点更新,以及healthd读取节点的实现,都比较简单.而其余部分比较关键. 特别注意init监控pr ...
- (Java实现) 洛谷 P1605 迷宫
题目背景 迷宫 [问题描述] 给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过.给定起点坐标和 终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案.在迷宫 中移动有上下 ...
- Java实现 LeetCode 732 我的日程安排表 III(暴力 || 二叉树)
732. 我的日程安排表 III 实现一个 MyCalendar 类来存放你的日程安排,你可以一直添加新的日程安排. MyCalendar 有一个 book(int start, int end)方法 ...
- java实现第四届蓝桥杯公式求值
公式求值 输入n, m, k,输出图1所示的公式的值.其中C_n^m是组合数,表示在n个人的集合中选出m个人组成一个集合的方案数.组合数的计算公式如图2所示. 输入的第一行包含一个整数n:第二行包含一 ...
- 【CSS】滚动条样式
/*定义滚动条宽高及背景,宽高分别对应横竖滚动条的尺寸*/ .scrollbar::-webkit-scrollbar{ width: 16px; height: 16px; background-c ...
- Jmeter之Json提取器详解(史上最全)
参考资料:https://www.bbsmax.com/A/D854lmBw5E/ Jsonpath在线测试:http://jsonpath.com/ 实际工作中用到的一些场景: 提取某个特定的值 提 ...
- Flutter upgrade更新版本引发的无法启动调试APP的错误 target:kernel_snapshot failed”
前言 我的主机上的Flutter 本地的分支是在 beta,因为去年想尝鲜Flutter Web,所以一直没切回来stable分支. 早上打开VSCode,右下角弹出了Flutter upgrade的 ...
- Windows下搭建Apache网站
目录 Apache下载 Apache安装 httpd.conf文件格式说明 启动服务并测试 Apache下载 在Apache官网底部找到APACHE PROJECT LIST里的HTTP Server ...
- Vue3 新特性
一.vue3 为什么要重写 两个主要原因考虑重写vue新版本主要功能: 1.主流浏览器对新的JavaScript语言特性的普遍支持. 2.当前Vue代码库随着时间的推移而暴露出来的设计和体系架构问题. ...