集合对象的编码可以是ziplist或者skiplist。
ziplist编码类型
每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个节点则保存元素的分值。
压缩列表内的集合元素按分值从小到大进行排序,分值较小的元素被防止在靠近表头的位置,而分值较大的元素则被放置在靠近表尾的位置。
skiplist编码类型
skiplist编码并不是只使用skiplist,而是skiplist和字典的集合,该集合命名为zset,定义见下:
typedef struct zset {
dict *dict;
zskiplist *zsl;
} zset;
zset结构中的zsl跳跃表按照分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素:跳跃表节点的object属性保存了元素的成员,而跳跃表节点的score属性则保存了元素的分值。通过这个跳跃表,程序可以对有序集合进行范围操作,例如zrank、zrange等命令。
zset中的dict字典,为有序集合创建了一个从成员到分值的映射,字典的键保存了元素的成员,而字典的值则保存了元素的分值。通过这个字典,程序可以用O(1)复杂度查找给定成员的分值,zscore就是通过这个特性实现的。
看起来两个结构似乎有了两份数据,但是实际上两个对象会通过指针共享数据,所以不存在冗余数据的问题。
这里两种结构的混合使用实际上就是一种空间换时间的典型案例。
对象类型的结构图
-
压缩列表编码类型
压缩列表对象.PNG
-
跳跃表编码类型
zset对象.PNG
对象中不同类型的适用条件:
只有同时满足以下两个条件时才会使用ziplist编码:
- 有序集合保存的元素数量小于128个;
- 有序结合保存的所有元素成员的长度都小于64字节;
不能满足以上条件的有序集合对象都将使用skiplist编码,这两个上限都可修改
通过以下命令验证:
127.0.0.1:6379> eval "for i=1, 128 do redis.call('zadd', KEYS[1], i, i) end" 1 znum
(nil)
127.0.0.1:6379> zcard znum
(integer) 128
127.0.0.1:6379> object encoding znum
"ziplist"
127.0.0.1:6379> zadd znum 3.14 pi
(integer) 1
127.0.0.1:6379> zcard znum
(integer) 129
127.0.0.1:6379> object encoding znum
"skiplist"
127.0.0.1:6379> zadd blah 1.9 www
(integer) 1
127.0.0.1:6379> object encoding blah
"ziplist"
127.0.0.1:6379> zadd blah 2.0 ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
(integer) 1
127.0.0.1:6379> object encoding blah
"skiplist"
网友评论