Redis数据结构(2)——列表

列表

内部编码:

  • ziplist(压缩列表):当列表元素个数小于 list-max-ziplist-entries 配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时(默认64个字节),redis会选用ziplist来作用列表内部实现(3.2版本前)
  • linkedlist(双向链表):当列表类型无法满足ziplist条件时(3.2版本前)
  • quicklist(快速列表):3.2版本及之后提供的新数据结构,中和ziplist和linkedlist的优缺点

作用:链表、发布与订阅、慢查询、监视器

linkedlist实现

linkedlist节点代码:

typedef struct listNode {
		// 前置节点
    struct listNode *prev;
		// 后置节点
    struct listNode *next;
		// 节点的值
    void *value;
}listNode;

链表linkedlist结构:

typedef struct list {
		// 表头节点
    listNode *head;
		// 表尾节点
    listNode *tail;
		// 链表所包含的节点数量
    unsigned long len;
		// 节点值复制函数
    void *(*dup)(void *ptr);
		// 节点值释放函数
    void *(*free)(void *ptr);
		// 节点值对比函数
    int (*match)(void *ptr, void *key);
} list;

链表linkedlist示意图:

quicklist实现

早期版本在这边选择的折中方案是两种数据类型的转换,但是在3.2版本之后,引入了一种新的数据格式,结合了双向列表linkedlist和ziplist的特点,称之为quicklist(快速列表)。所有的节点都用quicklist存储,省去了到临界条件是的格式转换。

那么quicklist是一种什么样的格式呢?简单的说,我们仍旧可以将其看作一个双向列表,但是列表的每个节点都是一个ziplist,其实就是linkedlist和ziplist的结合。quicklist中的每个节点ziplist都能够存储多个数据元素。

quicklist的定义如下:

typedef struct quicklist {
	quicklistNode *head;        // 指向quicklist的头部
	quicklistNode *tail;        // 指向quicklist的尾部
	unsigned long count;        // 列表中所有数据项的个数总和
	unsigned int len;           // quicklist节点的个数,即ziplist的个数
	int fill : 16;              // ziplist大小限定,由list-max-ziplist-size给定
	unsigned int compress : 16; // 节点压缩深度设置,由list-compress-depth给定
} quicklist;

可以看到,这边其实有两个统计值,count用来统计所有数据项的个数总和,len用来统计quicklist的节点个数, 因为每个节点ziplist都能存储多个数据项,所以有了这两个统计值。

quicklist的节点node的定义如下:

quicklist的节点node的定义如下:
		typedef struct quicklistNode {
		    struct quicklistNode *prev;  // 指向上一个ziplist节点
		    struct quicklistNode *next;  // 指向下一个ziplist节点
		    unsigned char *zl;           // 数据指针,如果没有被压缩,就指向ziplist结构,反之指向quicklistLZF结构// 
		    unsigned int sz;             // 表示指向ziplist结构的总长度(内存占用长度)
		    unsigned int count : 16;     // 表示ziplist中的数据项个数
		    unsigned int encoding : 2;   // 编码方式,1--ziplist,2--quicklistLZF
		    unsigned int container : 2;  // 预留字段,存放数据的方式,1--NONE,2--ziplist
		    unsigned int recompress : 1; // 解压标记,当查看一个被压缩的数据时,需要暂时解压,标记此参数为1,之后再重新进行压缩//
		    unsigned int attempted_compress : 1; // 测试相关
		    unsigned int extra : 10; // 扩展字段,暂时没用
} quicklistNode;

通过quicklist 将quicklistNode连接起来就是一个完整的双向列表了。为了进一步节约空间,Redis 还会对 ziplist 进行压缩存储,使用 LZF 算法压缩,可以选择压缩深度(quicklist compress字段表示)。

每个 quicklistNode存多少元素?

由于quicklist结构包含了压缩表和链表,那么每个quicklistNode的大小就是一个需要仔细考量的点。如果单个quicklistNode存储的数据太多,就会影响插入效率;但是如果单个quicklistNode太小,就会变得跟链表一样造成空间浪费。

quicklist 内部默认单个 ziplist 长度为 8k 字节,超出了这个字节数,就会新起一个 ziplist。ziplist 的长度由配置参数list-max-ziplist-size决定。

quicklist通过fill对单个quicklistNode的大小进行限制:fill可以被赋值为正整数或负整数:

  • -1:单个节点最多存储4kb
  • -2:单个节点最多存储8kb
  • -3:单个节点最多存储16kb
  • -4:单个节点最多存储32kb
  • -5:单个节点最多存储64kb
  • 为正数时,表示单个节点最大允许的元素个数,最大为32768个

压缩深度:

上文中提到了一个LZF的压缩算法,该算法用于对quicklist的节点进行压缩操作。list的设计目的是能够存放很长的数据列表,当列表很长时,必然会占用很高的内存空间,且list中最容易访问的是两端的数据,中间的数据访问率较低,于是就可以从这个出发点来进一步节省内存用于其他操作。Redis提供了一下的配置参数,用于表示中间节点是否压缩(quicklist compress字段表示)。

	list-compress-depth 0  //默认0
  • 0 特殊值,表示不压缩
  • 1 表示quicklist两端各有一个节点不压缩,中间的节点压缩
  • 2 表示quicklist两端各有两个节点不压缩,中间的节点压缩
  • 3 表示quicklist两端各有三个节点不压缩,中间的节点压缩
  • 以此类推。

quicklist结构示意图:

原创文章 15 获赞 22 访问量 6943

猜你喜欢

转载自blog.csdn.net/qq_31387317/article/details/91971327
今日推荐