本文内容均来自《Redis设计与实现》一书
有序集合对象的编码可以是ziplist或者skiplist。
1.定义
ziplist编码的有序集合对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员(member),第二个节点保存元素的分值(score)。
压缩列表内的集合元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的位置,而分值较大的元素被放置在靠近表尾的位置。
skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表。
// zset结构
typedef struct zset {
// 跳跃表
zskiplist *zsl;
// 字典
dict *dict;
} zset;
zset结构中的zsl跳跃表按分值从小到大保存了所有集合元素,每个跳跃表节点都保存一个集合元素:跳跃表节点的object属性保存了元素的成员,而跳跃表节点的score属性保存了元素的分值。通过这个跳跃表,程序可以对有序集合进行范围型操作,比如ZRANK、ZRANGE等命令就是基于跳跃表API来实现的。
zset结构中的dict字典为有序集合创建了一个从成员到分值的映射,字典中的每个键值对都保存了一个集合元素:字典的键保存了元素的成员,而字典的值保存了元素的分值。通过这个字典,程序可以用O(1)复杂度查找给定成员的分值,ZSCORE命令就是根据这一特性实现的,而很多其他有序集合命令都在实现的内部用到了这一特性。
有序集合每个元素的成员都是一个字符串对象,而每个元素的分值都是一个double类型的浮点数。虽然zset结构同时使用跳跃表和字典来保存有序集合的元素,但这两种数据结构都会通过指针来共享相同元素的成员和分值,所以同时使用跳跃表和字典来保存集合元素不会产生任何重复成员或者分值,也不会因此浪费额外的内存。
2.编码转换
当有序集合对象同时满足以下两个条件时,对象使用ziplist编码:
- 有序集合保存的元素数量小于128个。
- 有序集合保存的所有元素成员的长度都小于64字节。
不能同时满足以上两个条件的有序集合对象将使用skiplist编码。
注意:以上两个条件的上限值是可以修改的,对应的配置选项为zset-max-ziplist-entries和zset-max-ziplist-value。