✅Redis的ZipList、SkipList和ListPack之间有什么区别?
典型回答
之所以把他们三个放在一起比较,主要是因为他们都是实现ZSet的主要数据结构。
在Redis 7.0之前,ZSet主要靠ZipList和SkipList实现。而在Redis 7.0开始,ZipList已经被ListPack给替代了,也就是说Redis 7.0之后,ZSet主要靠ListPack和SkipList实现。
这里SkipList是一直在的,先介绍他。
SkipList
SkipList就是跳表。跳表是一种在链表的基础上增加多层索引的结构。跳表的结构通过多层索引链表来提高查找效率,相比于传统的链表,它能够在对数时间内完成元素的查找、插入和删除。
跳表的优势就是插入、删除、查找操作都可以在 O(log N) 的时间复杂度内完成。并且支持高效的范围查询,如 **ZRANGEBYSCORE**、**ZRANGE**** 等。**
但是跳表有个缺点,那就是他的数据结构决定了他的内存占用更高。每个元素有多个指针来维护多层链表,导致内存开销更大。
ZipList
而ZipList是一个压缩的数据结构,它的每个元素都是连续存储的,因此内存的使用非常紧凑。与其他数据结构相比,ZipList在小规模数据存储时显著减少了内存占用。
但是ZipList也有缺点,正因为他是紧凑的线性结构,所以如果在 ZipList 中查找一个元素时,可能需要遍历整个列表,同理插入和删除操作也是线性的。所以ZipList的插入、删除和查找操作的时间复杂度通常是 O(N)。相对来说是比较慢的。
这也是为什么Redis会在元素数量比较少的时候用ZipList,而在数据量大了之后转成SkipList的原因。
✅ZSet为什么在数据量少的时候用ZipList,而在数据量大的时候转成SkipList?
而且,因为ZipList的结构导致了,它在极端情况下可能会出现一种级联更新的问题:
所以就有了ListPack
ListPack
ListPack是 Redis 在 5.0 版本引入的一种新的内存高效数据结构(在Redis 7.0正式替代ZipList用在ZSet中),它是为了解决 ziplist 和 skiplist 在一些场景下的不足而提出的。
listpack 是一种 适用于小型有序集合、列表和哈希的压缩数据结构,它比 ziplist 更加灵活、适应性更强,并且提供了对大数据量的更好支持。
更重要的事,ListPack通过创新的数据结构方案,避免了级联更新的问题。
对比总结
| 特性 | SkipList (跳表) | ZipList (压缩列表) | ListPack (紧凑列表) |
|---|---|---|---|
| 设计目标 | 高效范围查询和有序访问 | 内存紧凑,减少碎片 | 解决级联更新问题 |
| 内存布局 | 多层链表结构 | 连续内存块 | 连续内存块 |
| 查询复杂度 | O(log N) | O(n) | O(n) |
| 插入复杂度 | O(log N) | O(1)~O(n²) (级联更新) | O(1) (无级联更新) |
| 删除复杂度 | O(log N) | O(1)~O(n²) (级联更新) | O(1) (无级联更新) |
| 内存占用 | 高 (有指针开销) | 低 | 极低 |
| 版本支持 | 所有版本 | Redis ≤6.2 | Redis ≥5.0 (7.0+默认) |
| 核心优势 | 高效有序访问 | 小数据内存优化 | 无级联更新+内存紧凑 |
| 主要缺点 | 内存占用高 | 级联更新 | 范围查询效率低 |
| 应用场景 | 有序集合(ZSet) | 小规模Hash/Set/ZSet | 全类型小规模存储 |