Sorted-set类型的value对象内部以ziplist或skiplist+hashtable来实现。
作为ziplist的实现方式和map类似。由于sorted-set包含了score的排 序信息,ziplist内部的key-value元素对的排序方式也是按照score顺序递增排序的,意味着每次新元素的插入都需要移动所有排在它之后的元素。因此ziplist适用于元素个数不多、元素内容不大的场景。
对于更通用的场景,sorted-set采用skiplist(跳表)来实现。
和通用的跳表实现不同,Redis为每一个level对象增加了span字段,表示该level指向的forward节点和当前节点的距离,使得getByRank类的操作效率提升,skiplist定义如下:
typedef struct skiplist {
struct zskiplistNode * header, *tail;
unsigned long length;
int level;
} zskiplist;
一个典型的Redis中的skipList结构如图7-7所示:
每次向skiplist中新增或删除一个元素(如图灰色的元素),需要同时修改图中标粗的箭头,修改其forward指针和span字段值。可以发现,需要修改的箭头和对skip进行查找操作遍历并废弃过的路径是吻合的,对于span的修改仅仅是加1或者减1(取决于本次操作是新增还是删除)。skiplist的查找复杂度平均是O(Log(N)),因此add/remove的复杂度也是O(Log(N))。因此redis新增的span字段并没有带来更多的复杂度和性能牺牲,同时提升了rank类操作的速度:求一某个元素在skiplist中的排名,仅需要将遍历路径上的span相加。
skiplist每个节点的高度随机生成,这点和通用的skiplist实现是类似的。