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;
}

随机推荐

  1. Quick-Cocos2d-x初学者游戏教程1

    Quick-Coco2d-x安装: Quick 安装完成后,在它的根目录下可以找到有两个名为setup_mac.sh.setup_win.bat的批处理脚本,它们分别是搭建Mac和Windows开发环 ...

  2. codeforces 477B B. Dreamoon and Sets(构造)

    题目链接: B. Dreamoon and Sets time limit per test 1 second memory limit per test 256 megabytes input st ...

  3. 【转】从Go、Swift语言出发

    Google于2009年第一次提出了Go的构思,Facebook在去年春天引入了Hack,随后不久Apple也发布了其Swift语言. 在战争中,胜利者写历史书:在科技中,赢的公司都在写编程语言.互联 ...

  4. Page_Load 事件

    Page_Load 事件是众多 ASP.NET 可理解的事件之一.Page_Load 事件会在页面加载时被触发,然后 ASP.NET 会自动调用子例程 Page_Load<%@ Page Lan ...

  5. 动态调整对话框属性(去掉标题栏,去掉边框,修改类似成Border:NONE样式)(调用ModifyStyle和ModifyStyleEx,然后调用SetWindowPos重新显示)

    // 动态修改对话框属性,去掉对话框标题栏,设置Border为NONE属性. if(dlg.GetSafeHwnd()) { dlg.ModifyStyle(WS_CAPTION, 0); // 去掉 ...

  6. streams 日差管理及监控

    第一部分 stream环境的日常管理 1.capture进程管理 --capture进程信息 SET LINESIZE 200 COLUMN CAPTURE_NAME HEADING 'Capture ...

  7. 一切不以用户为中心的O2O 都是耍流氓

    今天去万达广场逛街,手机搜了下附近的Wifi,发现有万达的免费Wifi,想起前些日子网上说万达要做O2O的试运营,于是尝试连接了下,结果弹出页面,要输入手机号,然后发送验证码才能登陆,结果输入手机号, ...

  8. MIB Browser如何导入已编译的mib

    开发过程中,如果需要来回在几套mib之间来回切换,每次都进行编译工作那将是一件很繁琐的事情,我们可以直接导入已经编译好的mib文件,避免重复的编译工作. 第一步,备份已经编译好的mib文件.    将 ...

  9. python--协程之特别篇

    Python通过yield提供了对协程的基本支持,但是不完全.而第三方的gevent为Python提供了比较完善的协程支持. gevent是第三方库,通过greenlet实现协程,其基本思想是: 当一 ...

  10. springbank 开发日志 springbank是如何执行一个handler的requestMapping对应的方法的

    占位 从dispatcher说起,方法doDispatch(Map request)的参数request是一个通过解析来报报文新城的map //获取HandlerExecutionChain,其中封装 ...