参考:<<Redis设计与实现>>
- 注:这本书是基于Redis3.0版本写的,和后面的版本有点差异
有序集合对象的编码是:
- ziplist
- skiplist
一、有序集合对象的编码
1.1 ziplist编码
ziplist编码的有序集合对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员(member),第二个元素保存元素的分值(score)。
压缩列表内的集合元素按分值从小到大排序,分值较小的元素被放置在靠近表头的位置,分值较大的元素被放置在靠近表尾的位置。
示例:
redis> zadd price 8.5 apple 5.0 banana 6.0 cherry
如果price键的值对象使用的是ziplist编码,值对象、以及对象使用的压缩列表如下图所示:
1.2 skiplist编码
skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表。
(1)zsl跳跃表
zset结构中的zsl跳跃表按分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素:
- 跳跃表节点的object属性保存了元素的成员
- 跳跃表节点的score属性保存了元素的分值
通过这个跳跃表,程序可以对有序集合进行范围型操作,比如zrank、zrange等命令就是基于跳跃表api来实现的。
(2)dict字典
zset结构中的dict字典为有序集合创建了一个从成员到分值的映射,字典中的每个键值对都保存了一个集合元素:
- 字典的键保存了元素的成员
- 字典的值保存了元素的分值
通过这个字典,程序可以用O(1)复杂度查找给定成员的分值,zscore命令根据这一特性实现的。
注:跳跃表和字典两种数据结构会通过指针共享相同元素的成员和分值。
(3)使用skiplist编码的有序集合对象、对象所使用的zset结构如下图所示:
二、编码的转换
有序集合对象同时满足以下2个条件时,对象使用ziplist编码,否则使用skiplist编码。
- 有序集合保存的元素数量小于128个
- 有序集合保存的所有元素成员的长度都小于64字节
注:上面的128、64阈值是可以修改的,可以查看zset-max-ziplist-entries
和zset-max-ziplist-value
选项。
redis> config get zset-max-ziplist-entries
1) "zset-max-ziplist-entries"
2) "128"
redis> config get zset-max-ziplist-value
1) "zset-max-ziplist-value"
2) "64"
2.1 ziplist -> skiplist
当使用ziplist编码的对象所需的两个条件任意一个不满足时,都会执行编码转换工作,将原本存储在压缩列表里面的所有集合元素转移到zset里,并将编码改为skiplist。
(1)元素数量超过阈值
临时设置zset-max-ziplist-entries
为4进行测试。
redis> config set zset-max-ziplist-entries 4
OK
redis> zadd nums 1 1 2 2 3 3 4 4
(integer) 4
redis> object encoding nums
"ziplist"
redis> zadd nums 5 5
(integer) 1
redis> zcard nums
(integer) 5
redis> object encoding nums
"skiplist"
(2)元素成员过长
临时设置zset-max-ziplist-value
为4进行测试。
redis> config set zset-max-ziplist-value 4
OK
redis> zadd strs 1 a 2 bb 3 ccc 4 dddd
(integer) 4
redis> object encoding strs
"ziplist"
redis> zadd strs 5 eeeee
(integer) 1
redis> object encoding strs
"skiplist"