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(快速列表)源码解析的更多相关文章

  1. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  2. Java 集合系列07之 Stack详细介绍(源码解析)和使用示例

    概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...

  3. Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...

  4. Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...

  5. 【转】Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...

  6. 【转】 Java 集合系列07之 Stack详细介绍(源码解析)和使用示例

    概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...

  7. Spring Cloud系列(四):Eureka源码解析之客户端

    一.自动装配 1.根据自动装配原理(详见:Spring Boot系列(二):Spring Boot自动装配原理解析),找到spring-cloud-netflix-eureka-client.jar的 ...

  8. 设置ZooKeeper服务器地址列表源码解析及扩展

    设置ZooKeeper服务器地址列表源码解析及扩展 ZooKeeper zooKeeper = new ZooKeeper("192.168.109.130:2181",SESSI ...

  9. Spring系列(六):Spring事务源码解析

    一.事务概述 1.1 什么是事务 事务是一组原子性的SQL查询,或者说是一个独立的工作单元.要么全部执行,要么全部不执行. 1.2 事务的特性(ACID) ①原子性(atomicity) 一个事务必须 ...

  10. 【转】Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

随机推荐

  1. Java实现 LeetCode 793 阶乘函数后K个零 (分析)

    793. 阶乘函数后K个零 f(x) 是 x! 末尾是0的数量.(回想一下 x! = 1 * 2 * 3 * - * x,且0! = 1) 例如, f(3) = 0 ,因为3! = 6的末尾没有0:而 ...

  2. Java实现 蓝桥杯 算法提高 字符串匹配

    试题 算法提高 字符串匹配 问题描述 给出一个字符串和多行文字,在这些文字中找到字符串出现的那些行.你的程序还需支持大小写敏感选项:当选项打开时,表示同一个字母的大写和小写看作不同的字符:当选项关闭时 ...

  3. Java实现 LeetCode 753 破解保险箱(递归)

    753. 破解保险箱 有一个需要密码才能打开的保险箱.密码是 n 位数, 密码的每一位是 k 位序列 0, 1, -, k-1 中的一个 . 你可以随意输入密码,保险箱会自动记住最后 n 位输入,如果 ...

  4. Java实现 LeetCode 503 下一个更大元素 II

    503. 下一个更大元素 II 给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素.数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大 ...

  5. Java实现 蓝桥杯 算法训练 约数个数

    算法提高 约数个数 时间限制:1.0s 内存限制:512.0MB 输入一个正整数N (1 样例输入 12 样例输出 6 样例说明 12的约数包括:1,2,3,4,6,12.共6个 import jav ...

  6. Java实现 蓝桥杯VIP 算法训练 和为T

    问题描述 从一个大小为n的整数集中选取一些元素,使得它们的和等于给定的值T.每个元素限选一次,不能一个都不选. 输入格式 第一行一个正整数n,表示整数集内元素的个数. 第二行n个整数,用空格隔开. 第 ...

  7. Java实现 LeetCode 54 螺旋矩阵

    54. 螺旋矩阵 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素. 示例 1: 输入: [ [ 1, 2, 3 ], [ 4, 5, 6 ], ...

  8. excel表 更改后缀名 xlsx转成csv csv转换xlsx

    excel 转成CSV 步骤: 1.打开你需要转换的Excel表格.这里特别介绍一下多个数字的现象,我们知道,像身份证这些多位数字,在正常表格里都是会加上一个'来使其显示的,或者换成"文本& ...

  9. java实现第六届蓝桥杯胡同门牌号

    胡同门牌号 小明家住在一条胡同里.胡同里的门牌号都是连续的正整数,由于历史原因,最小的号码并不是从1开始排的. 有一天小明突然发现了有趣的事情: 如果除去小明家不算,胡同里的其它门牌号加起来,刚好是1 ...

  10. TZOJ 1214: 数据结构练习题――线性表操作

    描述 请你定义一个线性表,可以对表进行“在某个位置之前插入一个元素”.“删除某个位置的元素”.“清除所有元素”.“获取某个位置的元素”等操作.键盘输入一些命令,可以执行上述操作.本题中,线性表元素为整 ...