开篇

zset和set类似,不过zset是有序的集合,排序原因是:zset的每个元素都会关联一个 double 类型的分数,redis 正是通过分数来为集合中的成员进行从小到大的排序

基础命令

  • zadd key [score member]:添加一个或多个元素,或者更新已存在元素的分数
  • zcard key : 获取集合元素的数量
  • zcount key min max:返回指定区间分数的元素数量(分数)
  • zlexcount key min max: 计算指定字典区间内元素数量(元素)
  • zrange key start stop :返回指定区间的元素
myRedis:0>zadd key1 10 test1 20 test2 30 test3 40 test4
"4"

myRedis:0>zcard key1
"4"

myRedis:0>zcount key1 10 30
"3"

myRedis:0>zlexcount key1 [test2 [test4
"3"

myRedis:0>zrange key1 1 3
 1)  "test2"
 2)  "test3"
 3)  "test4"
  • zscore key member: 返回指定member的分数
  • zincrby key increment member:给指定的member增加increment
  • zrem key [member]:移除一个或多个元素
myRedis:0>zscore key1 test1
"10"

myRedis:0>zincrby key1 50  test1
"60"

myRedis:0>zscore key1 test1
"60"

myRedis:0>zrem key1 test2
"1"

myRedis:0>zrange key1 0  -1
 1)  "test3"
 2)  "test4"
 3)  "test1"
  • zremrangebyrank key start stop:移除给定的排名区间的所有元素
  • zremrangebyscore key min max:移除给定的分数区间的所有元素
myRedis:0>zadd key1 55 test6 62 test7 44 test8 59 test9
"4"
myRedis:0>zrange key1 0 -1
 1)  "test3"  // 30
 2)  "test4"  // 40
 3)  "test8"  // 44
 4)  "test6"  // 55
 5)  "test9"  // 59
 6)  "test1"  // 60
 7)  "test7"  // 62

myRedis:0>zremrangebyrank key1 0 1
"2"

myRedis:0>zrange key1 0 -1 
 1)  "test8"
 2)  "test6"
 3)  "test9"
 4)  "test1"
 5)  "test7"

myRedis:0>zremrangebyscore key1 50 60
"3"

myRedis:0>zrange key1 0 -1
 1)  "test8"
 2)  "test7"

存储编码

Redis中存储zset有两种编码:ziplist和skiplist

ziplist

ziplist再链表的篇章中已经详细介绍:Redis-List篇

这里需要注意的是,采用ziplist作为底层编码时,每个entry都包含元素的成员member和元素的分值score,并且ziplist按照分值从小到大进行排序

skiplist

skiplist称为跳跃表,也简称跳表,在满足一定条件时,redis使用skiplist作为底层存储编码

skiplist查找图解
  • 我们先看下普通的链表结构
    在这里插入图片描述
    普通链表查找只能遍历,链表不支持随机查找,不能使用二分法
  • 可以模拟二分法原理,把3 7 11 提取出来
    在这里插入图片描述
    如果,我们需要查找的时9,可以在模拟二分法的情况下我们可以先查 3 7 11 先确定范围,确定9在7~11区间内,既可直接定位到元素的位置
  • 如果数量比较多,也可以按照以上规则,抽取不同的数据来提高数据的层级,提高查找效率
    在这里插入图片描述
  • 把level的属性放到对应的元素上,可以看到如图结构
    在这里插入图片描述

每一个level层级都有指向下一个相同level层级元素的指针
筛选规则:(查找元素7)

  • 先查找第三层数据,确定区间范围 3~11
  • 再查找第二层数据,确定范围 5~9
  • 最后查找第一层数据,确定元素7的位置
level生成规则

level生成是随机的,并没有指定的规律,可以看下随机函数源码(t_zset.c):

创建新节点的时候根据幂次定律(power law)随机生成的一个介于1~32之间的数字

/* Returns a random level for the new skiplist node we are going to create.
 * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL
 * (both inclusive), with a powerlaw-alike distribution where higher
 * levels are less likely to be returned. */
int zslRandomLevel(void) {
    int level = 1;
    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
        level += 1;
    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}
源码分析

skiplist源码(server.h)

typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;
typedef struct zskiplist {
    struct zskiplistNode *header, *tail;  //跳跃表的头和尾节点   指针
    unsigned long length;  // 元素数量
    int level;  // 等级level
} zskiplist;

/* ZSETs use a specialized version of Skiplists */
typedef struct zskiplistNode {
    sds ele;  //元素
    double score;  //分值
    struct zskiplistNode *backward; //后退指针
    struct zskiplistLevel {
        struct zskiplistNode *forward; //前进指针
        unsigned long span;//当前节点到下一个节点的节点数
    } level[];
} zskiplistNode;

当Redis的zset使用了skiplist的编码时,数据结构为zset,zset是封装了dict和 zskiplist的结构

属性含义简介
level层级数组一个节点的元素可以拥有多个层,通过不同层级的指针来选择最快捷的路径提升访问速度
forward前进指针指向链表尾部方向元素的指针 ,每一层都有一个
backward后退指针指向链表头部方向元素的指针,只有一个
span跨度记录了两个节点之间的距离,null表示跨度为0
ele元素SDS对象
score分值节点的分值是一个double类型的浮点数,从小到大排序,不同的节点分值可以重复
skiplist存储图解

图解1:
在这里插入图片描述
图解2
在这里插入图片描述
备注:虽然图解看起来zskiplist和hashtable像是两套数据,只是为了画图解析方便,实际上他们是共享数据和分值,没有存储两份数据,也并没有浪费内存空间

skiplist的编码特殊性

skiplist(数据编码)的底层使用的是封装了dict(字典)和zskiplist(跳跃表结构)的zset结构

  • 如果单独使用dict,查询性能很高,但是无序
  • 如果单独使用zskiplist,顺序可以保证,但是查询虽然有level,但是查询效率仍然不如dict
  • 如果同时使用dict和zskiplist,底层共享基础数据,就可以兼顾查询性能和顺序

编码转换规则

  • 保存的元素个数小于128个(zset-max-ziplist-entries,可修改)
  • 所有元素的总长度小于64字节(zset-max-ziplist-value,可修改)

同时满足以上两个条件,用ziplist编码存储,例:

myRedis:0>object encoding key1
"ziplist"

基础篇结束语

从Redis总纲到Zset,每个篇章中从基础命令到存储结构,把Redis基本内容都介绍了一遍,到此基础篇已经完结,接下来的要介绍的是Redis的事务,内存回收,持久化相关的内容

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐