quicklist和listpack
2023-3-23
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property

quicklist

在 Redis 3.0 之前,List 对象的底层数据结构是双向链表或者压缩列表。然后在 Redis 3.2 的时候,List 对象的底层改由 quicklist 数据结构实现。
其实 quicklist 就是「双向链表 + 压缩列表」组合,因为一个 quicklist 就是一个链表,而链表中的每个元素又是一个压缩列表。虽然压缩列表是通过紧凑型的内存布局节省了内存开销,但是因为它的结构设计,如果保存的元素数量增加,或者元素变大了,压缩列表会有「连锁更新」的风险,一旦发生,会造成性能下降。
quicklist解决办法,通过控制每个链表节点中的压缩列表的大小或者元素个数,来规避连锁更新的问题。因为压缩列表元素越少或越小,连锁更新带来的影响就越小,从而提供了更好的访问性能。
 
quicklist的结构体跟链表的结构体类似,都包含了表头和表尾,区别在于quicklist的节点是quicklistNode
quicklistNode结构体里包含了前一个节点和下一个节点指针,这样每个quicklistNode形成了一个双向链表。但是链表节点的元素不再是单纯保存元素值,而是保存了一个压缩列表,所以quicklistNode结构体里有个指向压缩列表的指针*zl
notion image
在向quicklist添加一个元素的时候,不会像普通的链表那样,直接新建一个链表节点。而是会检查插入位置的压缩列表是否能容纳该元素,如果能容纳就直接保存到quicklistNode结构里的压缩列表,如果不能容纳,才会新建一个新的quicklistNode结构。
quicklist会控制quicklistNode结构里的压缩列表的大小或者元素个数,来规避潜在的连锁更新的风险,但是这并没有完全解决连锁更新的问题。
 

listpack

listpack采用了压缩列表的很多优秀的设计,比如还是用一块连续的内存空间来紧凑地保存数据,并且为了节省内存的开销,listpack节点会采用不同的编码方式保存不同大小的数据。
listpack结构:
notion image
listpack头包含两个属性,分别记录了listpack总字节数和元素数量,然后listpack末尾也有个结尾标识。图中的listpack entry 就是listpack的节点了。每个listpack节点结构如下:
notion image
主要包含三个方面内容:
  • encoding,定义该元素的编码类型,会对不同长度的整数和字符串进行编码
  • data,实际存放的数据
  • len,encoding+data的总长度
可以看到,listpack没有压缩列表中记录前一个节点长度的字段了,listpack只记录当前节点的长度,当向listpack加入一个新元素的时候,不会影响其他节点的长度字段的变化,从而避免了压缩列表的连锁更新问题
 
  • Redis
  • 跳跃表 skiplist对象
    目录