3.31--Redis之常用数据结构--压缩列表(总结篇)------加油呀

时间:2021-08-14 01:14:53

压缩列表的最大特点

它被设计成一种内存紧凑型的数据结构,占用一块连续的内存空间,不仅可以利用 CPU
缓存,而且会针对不同长度的数据,进行相应编码,这种方法可以有效地节省内存开销

压缩列表的缺陷:

1.不能保存过多的元素,否则查询效率就会降低;
2.新增或修改某个元素时,压缩列表占用的内存空间需要重新分配,甚至可能引发连锁更新的问题。

因此,Redis 对象(List 对象、Hash 对象、Zset 对象)包含的元素数量较少,或者元素值不大的情况才会使用压缩列表作为底层数据结构。

压缩列表结构设计

压缩列表是 Redis 为了节约内存而开发的,它是由连续内存块组成的顺序型数据结构,有点类似于数组

3.31--Redis之常用数据结构--压缩列表(总结篇)------加油呀
压缩列表在表头有三个字段:

  1. zlbytes,记录整个压缩列表占用对内存字节数;
    2.zltail,记录压缩列表「尾部」节点距离起始地址由多少字节,也就是列表尾的偏移量;
  2. zllen,记录压缩列表包含的节点数量;
  3. zlend,标记压缩列表的结束点,固定值 0xFF(十进制255)

查找定位第一个元素和最后一个元素,可以通过表头三个字段(zllen)的长度直接定位,复杂度是 O(1)。

查找其他元素时,只能逐个查找,此时的复杂度就是 O(N) 了,因此压缩列表不适合保存过多的元素

压缩列表节点(entry)的构成如下:
3.31--Redis之常用数据结构--压缩列表(总结篇)------加油呀

压缩列表节点包含三部分内容:

1.prevlen,记录了「前一个节点」的长度,目的是为了实现从后向前遍历;
2.encoding,记录了当前节点实际数据的「类型和长度」,类型主要有两种:字符串和整数。
3.data,记录了当前节点的实际数据,类型和长度都由 encoding 决定;

压缩列表里的每个节点中的 prevlen 属性都记录了「前一个节点的长度」,而且 prevlen 属性的空间大小跟前一个节点长度值有关,比如:

1.如果前一个节点的长度小于 254 字节,那么 prevlen 属性需要用 1 字节的空间来保存这个长度值;
2.如果前一个节点的长度大于等于 254 字节,那么 prevlen 属性需要用 5 字节的空间来保存这个长度值;

连锁更新问题

压缩列表新增某个元素或修改某个元素时,如果空间不不够,压缩列表占用的内存空间就需要重新分配。而当新插入的元素较大时,可能会导致后续元素的 prevlen 占用空间都发生变化,从而引起「连锁更新」问题,导致每个元素的空间都要重新分配,造成访问压缩列表性能的下降

假设一个压缩列表中有多个连续的、长度在 250~253 之间的节点,因为这些节点长度值小于 254 字节,所以 prevlen 属性需要用
1 字节的空间来保存这个长度值。

这时,如果将一个长度大于等于 254 字节的新节点加入到压缩列表的表头节点,就会产生的连续多次空间扩展操作

压缩列表只会用于保存的节点数量不多的场景,只要节点数量足够小,即使发生连锁更新,也是能接受的

Redis 针对压缩列表在设计上的不足,在后来的版本中,新增设计了两种数据结构:quicklist(Redis 3.2 引入) 和 listpack(Redis 5.0 引入)。这两种数据结构的设计目标,就是尽可能地保持压缩列表节省内存的优势,同时解决压缩列表的「连锁更新」的问题