目录

1、版本区别

2、压缩列表转化成双向链表条件

3、linkedlist

4、ziplist

5、quickList


1、版本区别

版本3.2之前,Redis 列表list使用两种数据结构作为底层实现:压缩列表ziplist、双向链表linkedlist。

因为双向链表占用的内存比压缩列表要多, 所以当创建新的列表键时, 列表会优先考虑使用压缩列表, 并且在有需要的时候, 才从压缩列表实现转换到双向链表实现。

版本3.2之后,重新引入 quicklist,列表的底层都由quicklist实现。

2、压缩列表转化成双向链表条件

创建新列表时 redis 默认使用 redis_encoding_ziplist 编码, 当以下任意一个条件被满足时, 列表会被转换成 redis_encoding_linkedlist 编码:

  • 试图往列表新添加一个字符串值,且这个字符串的长度超过 server.list_max_ziplist_value (默认值为 64 )。
  • ziplist 包含的节点超过 server.list_max_ziplist_entries (默认值为 512 )。

注意:这两个条件是可以修改的,在 redis.conf 中:

list-max-ziplist-value 64 
list-max-ziplist-entries 512

3、linkedlist

当链表entry数据超过512、或单个value 长度超过64,底层就会转化成linkedlist编码;linkedlist是标准的双向链表,Node节点包含prev和next指针,可以进行双向遍历;还保存了 head 和 tail 两个指针,因此,对链表的表头和表尾进行插入的复杂度都为 (1) —— 这是高效实现 LPUSH 、 RPOP、 RPOPLPUSH 等命令的关键。

4、ziplist

ziplist 是一个特殊的双向链表。特殊之处在于:没有维护双向指针:prev next;而是存储上一个 entry的长度和 当前entry的长度,通过长度推算下一个元素在什么地方。牺牲读取的性能,获得高效的存储空间,因为(简短字符串的情况)存储指针比存储entry长度 更费内存。这是典型的“时间换空间”。

ziplist使用局限性:字段、值比较小,才会用ziplist。

如何通过一个节点向前跳转到另一个节点?用指向当前节点的指针 e , 减去 前一个 entry的长度, 得出的结果就是指向前一个节点的地址 p

ziplist连锁更新问题

因为在ziplist中,每个zlentry都存储着前一个节点所占的字节数,而这个数值又是变长编码的。假设存在一个压缩列表,其包含e1、e2、e3、e4…..,e1节点的大小为253字节,那么e2.prevrawlen的大小为1字节,如果此时在e2与e1之间插入了一个新节点e_new,e_new编码后的整体长度(包含e1的长度)为254字节,此时e2.prevrawlen就需要扩充为5字节;如果e2的整体长度变化又引起了e3.prevrawlen的存储长度变化,那么e3也需要扩…….如此递归直到表尾节点或者某一个节点的prevrawlen本身长度可以容纳前一个节点的变化。其中每一次扩充都需要进行空间再分配操作。删除节点亦是如此,只要引起了操作节点之后的节点的prevrawlen的变化,都可能引起连锁更新。

连锁更新在最坏情况下需要进行N次空间再分配,而每次空间再分配的最坏时间复杂度为O(N),因此连锁更新的总体时间复杂度是O(N^2)。即使涉及连锁更新的时间复杂度这么高,但它能引起的性能问题的概率是极低的:需要列表中存在大量的节点长度接近254的zlentry。由于ziplist连锁更新的问题,也使得ziplist的优缺点极其明显;也使得后续Redis采取折中,替换了ziplist。

ziplist的主要优点是节省内存,但它上面的查找操作只能按顺序查找(可以是从前往后、也可以从后往前)。ziplist将数据按照一定规则编码在一块连续的内存区域,目的是节省内存,这种结构并不擅长做修改操作。一旦数据发生改动,就会引发内存realloc,可能导致内存拷贝。

5、quickList

quickList是一个ziplist组成的双向链表。每个节点使用ziplist来保存数据。本质上来说,quicklist里面保存着一个一个小的ziplist。quickList就是一个标准的双向链表的配置,有head 有tail;每一个节点是一个quicklistNode,包含prev和next指针。每一个quicklistNode 包含 一个ziplist,*zp 压缩链表里存储键值。所以quicklist是对ziplist进行一次封装,使用小块的ziplist来既保证了少使用内存,也保证了性能。