之前的文章我们曾总结到了Redis数据结构——链表Redis数据结构——压缩列表这两种数据结构,他们是Redis List(列表)对象的底层实现方式。但是考虑到链表的附加空间相对太高,prev 和 next 指针就要占去 16 个字节 (64bit 系统的指针是 8 个字节),另外每个节点的内存都是单独分配,会加剧内存的碎片化,影响内存管理效率。因此Redis3.2版本开始对列表数据结构进行了改造,使用 quicklist 代替了 ziplist 和 linkedlist.

一、基本结构

    quicklist 实际上是 zipList 和 linkedList 的混合体,它将 linkedList 按段切分,每一段使用 zipList 来紧凑存储,多个 zipList 之间使用双向指针串接起来。

typedef struct quicklistNode {
struct quicklistNode *prev; //上一个node节点
struct quicklistNode *next; //下一个node
unsigned char *zl; //保存的数据 压缩前ziplist 压缩后压缩的数据
unsigned int sz; /* ziplist size in bytes */
unsigned int count : 16; /* count of items in ziplist */
unsigned int encoding : 2; /* RAW==1 or LZF==2 */
unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */
unsigned int recompress : 1; /* was this node previous compressed? */
unsigned int attempted_compress : 1; /* node can't compress; too small */
unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
  • prev: 指向链表前一个节点的指针。
  • next: 指向链表后一个节点的指针。
  • zl: 数据指针。如果当前节点的数据没有压缩,那么它指向一个ziplist结构;否则,它指向一个quicklistLZF结构。
  • sz: 表示zl指向的ziplist的总大小(包括zlbytes, zltail, zllen, zlend和各个数据项)。需要注意的是:如果ziplist被压缩了,那么这个sz的值仍然是压缩前的ziplist大小。
  • count: 表示ziplist里面包含的数据项个数。这个字段只有16bit。稍后我们会一起计算一下这16bit是否够用。
  • encoding: 表示ziplist是否压缩了(以及用了哪个压缩算法)。目前只有两种取值:2表示被压缩了(而且用的是LZF压缩算法),1表示没有压缩。
  • container: 是一个预留字段。本来设计是用来表明一个quicklist节点下面是直接存数据,还是使用ziplist存数据,或者用其它的结构来存数据(用作一个数据容器,所以叫container)。但是,在目前的实现中,这个值是一个固定的值2,表示使用ziplist作为数据容器。
  • recompress: 当我们使用类似lindex这样的命令查看了某一项本来压缩的数据时,需要把数据暂时解压,这时就设置recompress=1做一个标记,等有机会再把数据重新压缩。
  • attempted_compress: 这个值只对Redis的自动化测试程序有用。我们不用管它。
  • extra: 其它扩展字段。目前Redis的实现里也没用上。
typedef struct quicklistLZF {
unsigned int sz; /* LZF size in bytes*/
char compressed[];
} quicklistLZF;

quicklistLZF结构表示一个被压缩过的ziplist。其中:

  • sz: 表示压缩后的ziplist大小。
  • compressed: 是个柔性数组(flexible array member),存放压缩后的ziplist字节数组。
typedef struct quicklist {
quicklistNode *head;
quicklistNode *tail;
unsigned long count; /* total count of all entries in all ziplists */
unsigned long len; /* number of quicklistNodes */
int fill : QL_FILL_BITS; /* fill factor for individual nodes */
unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */
unsigned int bookmark_count: QL_BM_BITS;
quicklistBookmark bookmarks[];
} quicklist;
  • head: 指向头节点(左侧第一个节点)的指针。
  • tail: 指向尾节点(右侧第一个节点)的指针。
  • count: 所有ziplist数据项的个数总和。
  • len: quicklist节点的个数。
  • fill: 16bit,ziplist大小设置,存放list-max-ziplist-size参数的值。
  • compress: 16bit,节点压缩深度设置,存放list-compress-depth参数的值。

二、常用操作

2.1 插入

quicklist可以选择在头部或者尾部进行插入(quicklistPushHeadquicklistPushTail),而不管是在头部还是尾部插入数据,都包含两种情况:

  • 如果头节点(或尾节点)上ziplist大小没有超过限制(即_quicklistNodeAllowInsert返回1),那么新数据被直接插入到ziplist中(调用ziplistPush)。
  • 如果头节点(或尾节点)上ziplist太大了,那么新创建一个quicklistNode节点(对应地也会新创建一个ziplist),然后把这个新创建的节点插入到quicklist双向链表中。

也可以从任意指定的位置插入。quicklistInsertAfterquicklistInsertBefore就是分别在指定位置后面和前面插入数据项。这种在任意指定位置插入数据的操作,要比在头部和尾部的进行插入要复杂一些。

  • 当插入位置所在的ziplist大小没有超过限制时,直接插入到ziplist中就好了;
  • 当插入位置所在的ziplist大小超过了限制,但插入的位置位于ziplist两端,并且相邻的quicklist链表节点的ziplist大小没有超过限制,那么就转而插入到相邻的那个quicklist链表节点的ziplist中;
  • 当插入位置所在的ziplist大小超过了限制,但插入的位置位于ziplist两端,并且相邻的quicklist链表节点的ziplist大小也超过限制,这时需要新创建一个quicklist链表节点插入。
  • 对于插入位置所在的ziplist大小超过了限制的其它情况(主要对应于在ziplist中间插入数据的情况),则需要把当前ziplist分裂为两个节点,然后再其中一个节点上插入数据。

2.2 查找

list的查找操作主要是对index的我们的quicklist的节点是由一个一个的ziplist构成的每个ziplist都有大小。所以我们就只需要先根据我们每个node的个数,从而找到对应的ziplist,调用ziplist的index就能成功找到。

2.3 删除

区间元素删除的函数是 quicklistDelRange

quicklist 在区间删除时,会先找到 start 所在的 quicklistNode,计算删除的元素是否小于要删除的 count,如果不满足删除的个数,则会移动至下一个 quicklistNode 继续删除,依次循环直到删除完成为止。

quicklistDelRange 函数的返回值为 int 类型,当返回 1 时表示成功的删除了指定区间的元素,返回 0 时表示没有删除任何元素。

2.4 其它

除了上面介绍的基本操作之外还有一些其它操作,大家可以尝试着根据链表和压缩列表的数据结构来分析一些quicklist这些操作的时间复杂度。

操作 时间复杂度
quicklistCreate:创建 quicklist
quicklistInsertAfter:在某个元素的后面添加数据
quicklistInsertBefore:在某个元素的前面添加数据
quicklistReplaceAtIndex:替换某个元素
quicklistDelEntry:删除单个元素
quicklistDelRange:删除区间元素
quicklistPushHead:头部插入元素
quicklistPushTail:尾部插入元素

小结

    Redis quicklist是Redis 3.2版本以后针对链表和压缩列表进行改造的一种数据结构,是 zipList 和 linkedList 的混合体,相对于链表它压缩了内存。进一步的提高了效率。

如果你有什么疑问,欢迎在评论区给我留言和分享,我会第一时间反馈!我们一起共同学习与进步!

参考

《Redis设计与实现》

《Redis开发与运维》

《Redis官方文档》

-----END-----

Redis数据结构——quicklist的更多相关文章

  1. Redis数据结构之quicklist

    本文及后续文章,Redis版本均是v3.2.8 我们在使用Redis对外暴露的list数据结构时,给我们带来极大的便利性.其底层实现所依赖的内部数据结构就是quicklist. 我们先来回忆下list ...

  2. Redis 数据结构的底层实现 (一) RealObject,embstr,sds,ziplist,quicklist

    一.realObject Redis使用 string list zset hash set 五大数据类型来存储键和值.在每次生成一个键值对时,都会生成两个对象,一个储存键一个储存值.redis定义了 ...

  3. Redis 数据结构与内存管理策略(上)

    Redis 数据结构与内存管理策略(上) 标签: Redis Redis数据结构 Redis内存管理策略 Redis数据类型 Redis类型映射 Redis 数据类型特点与使用场景 String.Li ...

  4. Redis数据结构之intset

    本文及后续文章,Redis版本均是v3.2.8 上篇文章<Redis数据结构之robj>,我们说到redis object数据结构,其有5中数据类型:OBJ_STRING,OBJ_LIST ...

  5. Redis数据结构之robj

    本文及后续文章,Redis版本均是v3.2.8 我们知道一个database内的这个映射关系是用一个dict来维护的.dict的key固定用一种数据结构来表达,这这数据结构就是动态字符串sds.而va ...

  6. Redis 数据结构之dict

    上篇文章<Redis数据结构概述>中,了解了常用数据结构.我们知道Redis以高效的方式实现了多种数据结构,因此把Redis看做为数据结构服务器也未尝不可.研究Redis的数据结构和正确. ...

  7. 【Redis】270- 你需要知道的那些 redis 数据结构

    本文出自「掘金社区」,欢迎戳「阅读原文」链接和作者进行技术交流 ?? 作者简介 世宇,一个喜欢吉他.MDD 摄影.自走棋的工程师,属于饿了么上海物流研发部.目前负责的是网格商圈.代理商基础产线,平时喜 ...

  8. 面试官:你看过Redis数据结构底层实现吗?

    面试中,redis也是很受面试官亲睐的一部分.我向在这里讲的是redis的底层数据结构,而不是你理解的五大数据结构.你有没有想过redis底层是怎样的数据结构呢,他们和我们java中的HashMap. ...

  9. 为了拿捏 Redis 数据结构,我画了 40 张图(完整版)

    大家好,我是小林. Redis 为什么那么快? 除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis 能高效的处理. ...

随机推荐

  1. VUE实现Studio管理后台(二):Slot实现选项卡tab切换效果,可自由填装内容

    作为RXEditor的主界面,Studio UI要使用大量的选项卡TAB切换,我梦想的TAB切换是可以自由填充内容的.可惜自己不会实现,只好在网上搜索一下,就跟现在你做的一样,看看有没有好事者实现了类 ...

  2. 前阿里数据库专家总结的MySQL里的各种锁(下篇)

    在上篇中,我们介绍了MySQL中的全局锁和表锁. 今天,我们专注于介绍一下行锁,这个在日常开发和面试中常常困扰我们的问题. 1.行锁基础 由于全局锁和表锁对增删改查的性能都会有较大影响,所以,我们自然 ...

  3. 统计 Django 项目的测试覆盖率

    作者:HelloGitHub-追梦人物 文中所涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 我们完成了对 blog 应用和 comment 应用这两个核心 app 的测试.现在 ...

  4. 前端开发: webStorm手机真机调试

    目录 1. 做好准备工作 2. 开始设置 3. 配置路径 4. 匹配路径设置 5. 设置完成,即可体验 文章首发微信公众号飙码人生 2018-05-21 18:21:15 1. 做好准备工作 安装we ...

  5. vue 动态加载图片路径报错解决方法

    最近遇到图片路径加载报错的问题 之前一直都是把图片放到assets的文件下的.总是报错,看到一些文章并且尝试成功了,特意记录下 首先先说明下vue-cli的assets和static的两个文件的区别, ...

  6. 一文详解Hexo+Github小白建站

    作者:玩世不恭的Coder时间:2020-03-08说明:本文为原创文章,未经允许不可转载,转载前请联系作者 一文详解Hexo+Github小白建站 前言 GitHub是一个面向开源及私有软件项目的托 ...

  7. unzip详解,Linux系统如何解压缩zip文件?

    通常在使用linux时会自带了unzip,但是在最小化安装之后,可能系统里就无法使用此命令了. yum list unzip 查看是否安装 如果没安装过就继续 yum install unzip 安装 ...

  8. 五分钟学Java:如何才能学好Java Web里这么多的技术

    原创声明 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 系列文章介绍 本文是<五分钟学Java>系列文章的一篇 本系列文章主要围绕Java程序员必须掌握的核心技能,结合我个人三年 ...

  9. C++ 删除字符串中的数字并重新按顺序排列

    #include <stdio.h> #include <string.h> char* Find_str(char* p) { ; i < strlen(p); i++ ...

  10. Object.keys()方法

    一.定义和用法 返回对象的可枚举属性和方法的名称.二.参数 obj:要返回器枚举自身属性的对象.三.返回值 返回一个所有元素为字符串的数组,其元素来自于从给定的obj里可直接枚举的属性.这些属性的顺序 ...