zset对象的结构可以是ziplist或skiplist

当同时满足以下两个条件时使用ziplist:

  1. 有序集合保存的元素小于128个
  2. 所有元素长度小于64字节
    注,以上条件上限值可修改。在配置文件中zset-max-ziplsit-entries和zset-max-ziplist-value。

当ziplist作为zset的底层存储结构时候,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个元素保存元素的分值。集合元素按分值从小到大排序,小的在表头,大的在表尾。

zadd price 8.5 apple 8.1 banana

键price的值对象使用的是ziplist,对象如图所示。
ziplist编码的有序集合对象.png

对象使用的压缩列表如图所示:
有序集合压缩列表中从小到大排序.png

当skiplist作为zset的底层存储结构的时候,使用skiplist按序保存元素及分值,使用dict来保存元素和分值的映射关系。

ziplist

ziplist使用压缩列表作为底层实现,每个元素用两个挨在一起的压缩列表节点来保存。第一个保存元素的成员member,第二个保存元素的分值score.

skiplist

skiplist底层使用zset结构实现。zset结构同时包含一个字典和一个跳跃表

typedef struct zset {
    zskiplist *zsl; //跳跃表
    dick *dick;
} zset;

zset结构中zsl跳跃表按分值从小到大保存所有集合元素,每个跳跃表节点保存1个集合元素:跳跃表节点的object属性保存元素的成员,跳跃表节点的score属性保存分值。通过跳跃表进行范围操作。

除此之外,zset结构中的dick字典属性为有序集合创建了一个成员-分值的映射,字典的键保存一个集合元素,值就是元素的分值。通过这个字典实现O(1)复杂度查找指定成员的分值,即zscore命令。

有序集合每个元素的成员是一个字符串对象,成员的分值是一个double类型的浮点数。

值得一提的是,set结构同时使用跳跃表和字典结构来保存有序集合元素,但是两个数据结构通过指针来共享相同元素的成员和分值,所以不会产生重复成员和分值,也不会浪费额外内存。

为什么要同时使用跳跃表和字典呢?因为如果只使用字典结构无法保证有序;如果只有跳跃表根据成员查找分值的时间复杂度将从O(1)升为O(logn)。

标签: none

添加新评论