有序集合类型 的对象编码方式为 OBJ_ENCODING_ZIPLIST 和 OBJ_ENCODING_SKIPLIST,对应的 底层数据结构为 压缩列表ziplist 和 跳跃表skiplist
zset 在使用 ziplist 作为底层数据结构的时候,每个 集合元素 使用紧挨着的两个压缩列表节点表示,第一个节点 保存元素 成员,第二个节点 保存元素 分值;压缩列表中集合元素都是按照 分值 从小到大 进行排序存储的,分值比较小的放在靠近 表头的方向
而 zset 在使用 skiplist 作为底层数据结构的时候,跳跃表存储部分 之前在 跳跃表 skiplist 篇 写的非常详细了,需要注意的是 每个 zset 结构都是 包含一个 数据字典 和 一个跳跃表
zset 数据结构中的 数据字典dict 部分为 有序集合 创建了一个从 成员到分值 的映射,字典中每个 键值对都保存了一个集合元素:键保存了元素成员,值保存了元素分值;有了这个字典,获取指定元素的分值的复杂度就是 O(1),比如 zscore 命令等
另外值得一提的是,虽然 zset 在上述情况中 同时 使用了 跳跃表 和 字典 来保存有序集合元素,但是两种数据都会通过指针来共享相同元素的成员和分值,所以即使 同时使用两种 存储方式,却也 不会 产生重复 的成员数据,不会浪费过多额外的内存
编码转换
跟 上一篇 的 集合类型 一样道理,有序集合类型 涉及到了 两个数据结构 压缩列表 ziplist 和 跳跃表 skiplist ,所以也会涉及 两个结构的编码转换
当 有序集合条件 可以同时满足以下条件的时候,对象使用 ziplist 结构,否则就使用 skiplist 结构 作为底层实现
- 有序集合保存的元素个数小于 128个(这个限制是由
zset-max-ziplist-entries
参数控制,对应 server 全局变量为server.zset_max_ziplist_entries
,默认 128) - 有序集合保存的所有元素成员的长度都小于 64 字节(这个限制是由
zset-max-ziplist-value
参数控制,对应 server 全局变量为server.zset_max_ziplist_value
,默认 64)
对于使用 ziplist 编码的有序集合对象来说,当使用 ziplist 编码所需的两条件任意一个不被满足,就会执行对象的 编码转换操作,原本保存在 压缩列表 ziplist 中的元素都会被转移到 zset 结构中,编码也会跟着变成 skiplist
下面我们就 Redis 有序集合类型 一些典型的 API 进行解析~~~
源码解析
由于有些方法里的代码量比较大,我们这里按照 典型的代码片段进行解析,同志们可以根据文章提示的代码位置 和 代码里面的关键词 在源码中搜素,可能数据结构一些元素 看不太懂什么意思,没关系,先混个脸熟,后面看完回头再看过来就明白了
首先我们上面提到的编码类型转换函数为 zsetConvert()
有序集合添加元素命令 zadd 使用的 函数为 zaddCommand()
,那么具体的逻辑在 zaddGenericCommand()
函数中,不过命令中有几个参数需要解释一下
XX
表示只有当元素存在的时候才更新其分值,不存在时不添加新元素NX
表示只添加新元素,如果存在则不作处理CH
修改返回值为发生变化的成员总数,原始是返回新添加成员的总数;更改的成员是新添加的成员,已经存在的成员更新分数;所以在命令中指定的成员有相同的分数的话,不计算在内;在通常情况下,ZADD返回值只计算新添加成员的数量。INCR
当ZADD指定这个选项时,成员的操作就等同ZINCRBY命令,对成员的分数进行递增操作
|
|
上面添加元素的逻辑中使用了一个 zsetAdd()
函数,具体逻辑如下
上述也用到了 在指定 ziplist 位置插入元素 的函数 zzlInsert()
,假定该元素不存在于该 ziplist中,其中元素按分值大小排序,如果分值相同,则按 字典里的顺序排序
以上为比较经典的函数逻辑解析,另外我从 其他资料找到了 一些 API 的详解,大家可以将就看看
本文作者: wettper
本文链接: http://www.web-lovers.com/redis-source-zset.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!