在 Redis 中,有序集合(ZSet) 的底层实现根据数据量和元素大小的不同,采用了两种编码方式:
-
跳跃表(Skiplist) + 字典(Hash Table):当有序集合的元素数量较多,或者每个元素的长度较大时,Redis 会使用跳跃表和字典的组合来实现 ZSet。
-
压缩列表(Ziplist):当有序集合的元素数量较少,且每个元素的长度较小时,Redis 会使用压缩列表来实现 ZSet。
跳跃表(Skiplist) 是一种基于多级索引的链表结构,能够在平均 O(log N) 的时间复杂度内完成查找、插入和删除操作。在 Redis 的 ZSet 中,跳跃表用于维护元素的有序性,支持高效的范围查询和排序操作。
字典(Hash Table) 用于存储元素值到分数(score)的映射关系,提供 O(1) 的查找时间复杂度。通过字典,Redis 可以快速地根据元素值获取其对应的分数,支持高效的元素查找和更新操作。
当 ZSet 的元素数量较少,且每个元素的长度较小时,Redis 会使用压缩列表(Ziplist)来实现 ZSet。压缩列表是一种紧凑的内存结构,适用于存储小型数据集合。在这种情况下,ZSet 的元素按分数从小到大排序,压缩列表中的每个节点包含元素值和对应的分数。这种方式节省内存,但在查找、插入和删除操作时,性能相对较低。
需要注意的是,Redis 会根据实际情况自动选择使用跳跃表和字典的组合,还是使用压缩列表。具体的选择标准是:
-
元素数量小于 128 个:使用压缩列表。
-
每个元素的长度小于 64 字节:使用压缩列表。
当以上条件不满足时,Redis 会使用跳跃表和字典的组合来实现 ZSet。
这种设计使得 Redis 在处理不同规模和类型的数据时,能够根据实际情况选择最合适的底层数据结构,从而在性能和内存占用之间取得良好的平衡。
在 Redis 中,有序集合(ZSet) 的底层实现主要采用 跳跃表(Skiplist),但并非在所有情况下都使用跳跃表。
为什么使用跳跃表?
-
实现简单:跳跃表的结构直观,易于理解和实现,相较于平衡树(如红黑树),其代码更为简洁,维护起来也更方便。 citeturn0search1
-
支持范围查询:跳跃表天然支持范围查询操作,例如获取某个分数区间内的所有元素,这对于有序集合的应用场景非常重要。 citeturn0search0
-
性能优越:跳跃表在插入、删除和查找操作上具有平均 O(log N) 的时间复杂度,性能表现良好。 citeturn0search2
-
内存占用可控:通过调整跳跃表的层数和节点的随机性,Redis 可以在内存占用和查询性能之间取得平衡。 citeturn0search1
是否一定使用跳跃表?
并非所有情况下 Redis 都使用跳跃表来实现 ZSet。
-
小数据量和短字符串:当 ZSet 中的元素数量较少,且每个元素的字符串长度较短时,Redis 会使用 压缩列表(Ziplist) 作为底层数据结构,以节省内存。 citeturn0search10
-
大数据量或长字符串:当 ZSet 中的元素数量较多,或者每个元素的字符串长度较长时,Redis 会选择使用跳跃表和字典的组合来实现 ZSet。 citeturn0search10
因此,Redis 会根据实际情况自动选择最适合的底层数据结构,以平衡性能和内存占用。