Redis 提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合),理解每种数据类型的特色对于redis的开发和运维很是重要。html
原文解析java
Redis 中的 list 是咱们常用到的一种数据类型,根据使用方式的不一样,能够应用到不少场景中。node
上节**《闲扯Redis四》List数据类型底层编码转换** 说道,在 3.0 版本的 Redis 中,List 类型有两种实现方式:redis
一、使用压缩列表(ziplist)实现的列表对象。算法
二、使用双端链表(linkedlist)实现的列表对象。数组
在 3.2 版本后新增了 quicklist 数据结构实现了 list,如今就来分析下 quicklist 的结构数据结构
先来看看 Redis 官方对 quicklist 的描述:运维
A doubly linked list of ziplists
A generic doubly linked quicklist implementation
复制代码
可见 quicklist 是一个双向链表,而且是一个 ziplist 的双向链表,也就是说 quicklist 的每一个节点都是一个 ziplist。而经过前面的文章我们能够知道,ziplist 自己也是一个能维持数据项前后顺序的列表,并且数据项保存在一个连续的内存块中。那是否是意味着 quicklist 结合了压缩列表和双端链表的特色呢!性能
/* * quicklist */
typedef struct quicklist {
//头结点
quicklistNode *head;
//尾节点
quicklistNode *tail;
//全部ziplist中entry数量
unsigned long count;
//quicklistNodes节点数量
unsigned int len;
//ziplist中entry能保存的数量,由list-max-ziplist-size配置项控制
int fill : 16;
//压缩深度,由list-compress-depth配置项控制
unsigned int compress : 16;
} quicklist;
复制代码
注释:ui
fill :ziplist 中 entry 能保存的数量,由 list-max-ziplist-size 配置项控制
表示了单个节点(quicklistNode)的负载比例(fill factor),负数限制 quicklistNode 中的 ziplist 的字节长度,
正数限制 quicklistNode 中的 ziplist 的最大长度。
复制代码
-5: 最大存储空间: 64 Kb <-- 一般状况下不要设置这个值
-4: 最大存储空间: 32 Kb <-- 很是不推荐
-3: 最大存储空间: 16 Kb <-- 不推荐
-2: 最大存储空间: 8 Kb <-- 推荐
-1: 最大存储空间: 4 Kb <-- 推荐
对于正整数则表示最多能存储到你设置的那个值, 当前的节点就装满了
一般在 -2 (8 Kb size) 或 -1 (4 Kb size) 时, 性能表现最好
复制代码
compress :压缩深度,由 list-compress-depth 配置项控制
表示 quicklist 中的节点 quicklistNode, 除开最两端的 compress 个节点以后, 中间的节点都会被压缩(LZF压缩算法)。
复制代码
typedef struct quicklistNode {
//前节点指针
struct quicklistNode *prev;
//后节点指针
struct quicklistNode *next;
//数据指针。当前节点的数据没有压缩,那么它指向一个ziplist结构;不然,它指向一个quicklistLZF结构。
unsigned char *zl;
//zl指向的ziplist实际占用内存大小。须要注意的是:若是ziplist被压缩了,那么这个sz的值仍然是压缩前的ziplist大小
unsigned int sz;
//ziplist里面包含的数据项个数
unsigned int count : 16;
//ziplist是否压缩。取值:1--ziplist,2--quicklistLZF
unsigned int encoding : 2;
//存储类型,目前使用固定值2 表示使用ziplist存储
unsigned int container : 2;
//当咱们使用相似lindex这样的命令查看了某一项原本压缩的数据时,须要把数据暂时解压,这时就设置recompress=1作一个标记,等有机会再把数据从新压缩
unsigned int recompress : 1;
unsigned int attempted_compress : 1; /* node can't compress; too small */
unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
复制代码
typedef struct quicklistLZF {
unsigned int sz; //压缩后的ziplist大小
char compressed[];//柔性数组,存放压缩后的ziplist字节数组
} quicklistLZF;
复制代码
根据上述结构体定义,我们能够绘制一下 quicklist 的结构:
1.双端链表便于在表的两端进行 push 和 pop 操做,可是它的内存开销比较大;
2.双端链表每一个节点上除了要保存数据以外,还要额外保存两个指针;
3.双端链表的各个节点是单独的内存块,地址不连续,节点多了容易产生内存碎片;
1.ziplist 因为是一整块连续内存,因此存储效率很高;
2.ziplist 不利于修改操做,每次数据变更都会引起一次内存的 realloc;
3.当 ziplist 长度很长的时候,一次 realloc 可能会致使大批量的数据拷贝,进一步下降性能;
1.空间效率和时间效率的折中;
2.结合了双端链表和压缩列表的优势;
![]()