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 ...
随机推荐
- Java实现 蓝桥杯 算法训练 大小写转换
算法训练 大小写转换 时间限制:1.0s 内存限制:512.0MB 提交此题 问题描述 编写一个程序,输入一个字符串(长度不超过20),然后把这个字符串内的每一个字符进行大小写变换,即将大写字母变成小 ...
- Java实现 LeetCode 433 最小基因变化
433. 一条基因序列由一个带有8个字符的字符串表示,其中每个字符都属于 "A", "C", "G", "T"中的任意一 ...
- Java实现 LeetCode 34 在排序数组中查找元素的第一个和最后一个位置
在排序数组中查找元素的第一个和最后一个位置 给定一个按照升序排列的整数数组 nums,和一个目标值 target.找出给定目标值在数组中的开始位置和结束位置. 你的算法时间复杂度必须是 O(log n ...
- Java实现 蓝桥杯 算法提高 双十一抢购
试题 算法提高 双十一抢购 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 一年一度的双十一又来了,某网购网站又开始了半价销售的活动. 小G打算在今年的双十一里尽情地购物,以享受购买 ...
- Java实现旅行商问题
1 问题描述 何为旅行商问题?按照非专业的说法,这个问题要求找出一条n个给定的城市间的最短路径,使我们在回到触发的城市之前,对每个城市都只访问一次.这样该问题就可以表述为求一个图的最短哈密顿回路的问题 ...
- java实现砝码称重
5个砝码 用天平称重时,我们希望用尽可能少的砝码组合称出尽可能多的重量. 如果只有5个砝码,重量分别是1,3,9,27,81.则它们可以组合称出1到121之间任意整数重量(砝码允许放在左右两个盘中). ...
- Java实现 蓝桥杯 历届试题 城市建设
问题描述 栋栋居住在一个繁华的C市中,然而,这个城市的道路大都年久失修.市长准备重新修一些路以方便市民,于是找到了栋栋,希望栋栋能帮助他. C市中有n个比较重要的地点,市长希望这些地点重点被考虑.现在 ...
- 流程图(HTML5拖拽)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Vue入门 — Vue + vuetifyjs应用实践
分享一个以前学vue时自己练手的一个小项目,项目使用vue-cli3创建,UI库用的是vuetifyjs,vuetifyjs官网:https://vuetifyjs.com/ 数据来源是网上随便找的一 ...
- 【网页设计】第四周 JavaSript
第四周 JSP 一 JSP概述 含义: Java Server Pages, 广泛使用的服务器端脚本语言之一:(运行在服务器端 BS结构) 由服务器端的JSP引擎执行JSP代码,然后将结果以HT ...