Redis ZSet底层跳表与压缩列表的切换条件
Redis的有序集合(Sorted Set,简称ZSet)是一种非常强大的数据结构,它结合了哈希表和跳表的优点,既能通过键快速定位,又能对成员进行排序。ZSet的底层实现并非一成不变,它会根据数据规模和元素大小,在压缩列表(ziplist)和跳表(skiplist)之间动态切换,以平衡内存占用和操作性能。
本文将手把手教你理解这两种底层结构,并掌握它们之间的切换条件。
1. 理解两种底层结构
在深入切换条件之前,你需要先了解这两种数据结构的基本特点。
1.1 压缩列表(ziplist)
压缩列表是一种为节约内存而设计的连续内存块数据结构。它类似于一个数组,将元素和它们的分数紧凑地存储在一起,没有指针,通过偏移量来定位元素。
- 优点: 内存占用极小,非常适合存储少量的小元素。
- 缺点: 查找、插入和删除操作的时间复杂度为O(N),当元素较多时性能会显著下降。
1.2 跳表(skiplist)
跳表是一种基于链表的概率性数据结构,通过多层索引来加速查找。它是一种平衡树的高效替代品。
- 优点: 查找、插入和删除操作的平均时间复杂度为O(log N),性能稳定,适合存储大量元素。
- 缺点: 相比压缩列表,内存占用更大。
2. 切换条件详解
Redis通过两个配置参数来决定ZSet何时从压缩列表切换到跳表,以及何时尝试从跳表切换回压缩列表。
2.1 从压缩列表切换到跳表
当满足以下任意一个条件时,Redis会将ZSet的底层结构从压缩列表(ziplist)升级为跳表(skiplist):
- 元素数量超过阈值: ZSet中的元素数量超过了
zset-max-ziplist-entries配置项的值。 - 元素大小超过阈值: ZSet中任意一个成员(或其分数)的大小超过了
zset-max-ziplist-value配置项的值。
这两个配置项的默认值分别是128和64字节。你可以通过Redis的配置文件redis.conf进行修改。
示例:
假设你有一个ZSet,其中包含129个元素,即使每个元素都非常小,也会触发从压缩列表到跳表的切换。
# 查看当前配置
CONFIG GET zset-max-ziplist-entries
CONFIG GET zset-max-ziplist-value
# 修改配置(需重启Redis或使用CONFIG SET命令动态修改)
CONFIG SET zset-max-ziplist-entries 200
CONFIG SET zset-max-ziplist-value 128
2.2 从跳表切换到压缩列表
与升级过程不同,从跳表降级回压缩列表的条件更为严格,并且不是自动发生的。Redis在特定情况下会尝试进行降级,但通常需要手动干预或等待元素被大量删除。
降级的条件是:
- 元素数量低于阈值: ZSet中的元素数量减少到
zset-max-ziplist-entries配置项的值以下。 - 所有元素大小低于阈值: ZSet中所有成员(及其分数)的大小都低于
zset-max-ziplist-value配置项的值。
即使同时满足以上两个条件,Redis也不会立即执行降级。降级操作通常在以下场景中发生:
- 手动执行: 使用
DEBUG RELOAD命令(不推荐,会重置所有数据)或通过特定的API(如Redis模块)。 - 元素被大量删除: 当你从ZSet中删除了大量元素,导致其规模显著缩小,Redis可能会在内部进行优化时触发降级。
3. 如何查看当前使用的结构
你可以使用OBJECT ENCODING命令来查看任意ZSet当前使用的底层编码方式。
- 如果输出是
ziplist,表示使用压缩列表。 - 如果输出是
skiplist,表示使用跳表。
操作步骤:
-
连接到Redis服务器:
redis-cli -
创建一个ZSet并添加元素:
ZADD myzset 1 "member1" ZADD myzset 2 "member2" -
查看编码:
OBJECT ENCODING myzset如果元素数量和大小都在阈值内,你会看到输出
ziplist。 -
添加更多元素触发切换:
# 假设阈值是128,我们添加129个元素 # 这里用循环添加129个元素,实际使用时请根据你的Redis版本和配置调整 -
再次查看编码:
OBJECT ENCODING myzset此时你应该会看到输出
skiplist。
4. 实践建议
理解切换条件后,你可以根据实际应用场景进行优化:
-
评估数据规模: 在设计应用时,预估ZSet的元素数量和大小。如果元素很少且很小,可以保留默认配置,利用压缩列表节省内存。如果元素很多或很大,可以考虑适当调高
zset-max-ziplist-entries和zset-max-ziplist-value的值,推迟切换到跳表的时间,或者在数据量很大时直接使用跳表。 -
监控内存使用: 对于内存敏感的应用,密切关注ZSet的编码方式。如果发现大量ZSet使用了跳表,而实际元素数量很少,可以考虑调整配置或重构数据模型。
-
避免频繁切换: 频繁地在两种结构之间切换会带来额外的性能开销。尽量让ZSet的数据规模保持相对稳定,避免大起大落。
通过掌握这些知识,你可以更好地利用Redis ZSet,优化你的应用性能和内存效率。

暂无评论,快来抢沙发吧!