Redisbook学习笔记(3)数据类型之有序集合

REDIS_ZSET (有序集) 是ZADD 、ZCOUNT 等命令的操做对象, 它使用redis

REDIS_ENCODING_ZIPLIST 和REDIS_ENCODING_SKIPLIST 两种方式编码:服务器

wKioL1MUfyPQe1glAACz8W_JBgE061.jpg

编码的选择数据结构

在经过ZADD 命令添加第一个元素到空key 时,程序经过检查输入的第一个元素来决定该创ide

建什么编码的有序集。函数

若是第一个元素符合如下条件的话,就建立一个REDIS_ENCODING_ZIPLIST 编码的有序集:编码

服务器属性server.zset_max_ziplist_entries 的值大于0 (默认为128 )。spa

元素的member 长度小于服务器属性server.zset_max_ziplist_value 的值(默认为64指针

)。server

不然,程序就建立一个REDIS_ENCODING_SKIPLIST 编码的有序集。对象

编码的转换

对于一个REDIS_ENCODING_ZIPLIST 编码的有序集,只要知足如下任一条件,就将它转换为

REDIS_ENCODING_SKIPLIST 编码:

ziplist 所保存的元素数量超过服务器属性server.zset_max_ziplist_entries 的值

(默认值为128 )

新添加元素的member 的长度大于服务器属性server.zset_max_ziplist_value 的值

(默认值为64 )

ZIPLIST 编码的有序集

当使用REDIS_ENCODING_ZIPLIST 编码时,有序集将元素保存到ziplist 数据结构里面。

其中,每一个有序集元素以两个相邻的ziplist 节点表示,第一个节点保存元素的member 域,

第二个元素保存元素的score 域。

多个元素之间按score 值从小到大排序,若是两个元素的score 相同,那么按字典序对member

进行对比,决定那个元素排在前面,那个元素排在后面。

wKioL1MUgA-jnP_6AACKQ-6svr0735.jpg

虽然元素是按score 域有序排序的,但对ziplist 的节点指针只能线性地移动,因此在

REDIS_ENCODING_ZIPLIST 编码的有序集中,查找某个给定元素的复杂度为O(N) 。

每次执行添加/删除/更新操做都须要执行一次查找元素的操做,所以这些函数的复杂度都不低

于O(N) ,至于这些操做的实际复杂度,取决于它们底层所执行的ziplist 操做。

SKIPLIST 编码的有序集

当使用REDIS_ENCODING_SKIPLIST 编码时,有序集元素由redis.h/zset 结构来保存:

/*
* 有序集
*/
typedef struct zset {
// 字典
dict *dict;
// 跳跃表
zskiplist *zsl;
} zset;

zset 同时使用字典和跳跃表两个数据结构来保存有序集元素。

其中,元素的成员由一个redisObject 结构表示,而元素的score 则是一个double 类型的浮

点数,字典和跳跃表两个结构经过将指针共同指向这两个值来节约空间(不用每一个元素都复制

两份)。

下图展现了一个REDIS_ENCODING_SKIPLIST 编码的有序集:

wKiom1MUgUmCXQhiAAGF3CPhasw285.jpg

经过使用字典结构,并将member 做为键,score 做为值,有序集能够在O(1) 复杂度内:

检查给定member 是否存在于有序集(被不少底层函数使用);

取出member 对应的score 值(实现ZSCORE 命令)。

另外一方面,经过使用跳跃表,能够让有序集支持如下两种操做:

在O(logN) 指望时间、O(N) 最坏时间内根据score 对member 进行定位(被不少底层

函数使用);

范围性查找和处理操做,这是(高效地)实现ZRANGE 、ZRANK 和ZINTERSTORE

等命令的关键。

经过同时使用字典和跳跃表,有序集能够高效地实现按成员查找和按顺序查找两种操做。

相关文章
相关标签/搜索