redis设计与实现(三)有序集合和集合的实现——跳跃表和整数集合

  1. 跳跃表是一种有序的数据结构
  2. 每个节点都维护多个指向其他节点的指针,达到快速访问节点的目的
  3. 平均查找复杂度为O(logN)、最坏为O(N),可通过顺序操作批量处理接地那。
  4. 大部分情况其效率可和平衡树媲美
  5. 每个跳跃表层高都是1到32之间的随机数

跳跃表在redis中用于有序集合的实现。另外也用在集群节点的内部数据结构。

一、跳跃表的实现

zskiplist

该结构保存了跳跃表节点的信息。跳跃表节点由zskiplistNode实现。

有如下属性:

  • header

    指向跳跃表的表头节点

  • tail

    指向表尾节点

  • level

    跳跃表当前最大的层数的节点的层数(除表头之外)

  • length

    跳跃表长度。即除表头之外的节点数量

因此直接定位表头和表尾节点的复杂度为O(1)。获取节点个数的复杂度也为O(1)

二、跳跃表节点

表节点由zskiplistNode实现。

typedef struct zskiplistNode{
    //前进指针
    struct skiplist *backward;
    //分值
    double score;
    //成员对象
    robj *obj;
    //层
    struct zskiplistLevel{
        //前进指针
        struct zskiplistNode *forward;
        unsigned int span;//跨度
    }level[];
    
}zskiplistNode;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A0aHIqcL-1588696729636)(assets/1588391991791.png)]

2.1后退指针

每个节点只有一个后退指针,因此只能一个个节点从后往前遍历。【上图的BW表示】

首先通过tail指针找到尾节点,再从尾节点的后退指针一步步往前遍历,直到遇到NULL,结束遍历。

而下面要讲的前进指针,每个节点可以有多个层,每个层都有一个前进指针,每个前进指针可以跨节点指向。

2.2 层

一个level[]数组表示。

2.2.1 前进指针

每层维护一个前进指针:指向位于表尾方向的其他节点,用于从表头向表尾方向访问节点。

  1. 首先访问表头节点,再从第四层的前进指针移到第二个节点
  2. 在第二个节点再从第二层的前进指针走到第三个节点
  3. 在第三个节点再从第二层的前进指针走到最后一个节点,即尾节点
  4. 从最后一个节点的第二层的前进指针移动,发现是NULL,就知道已经到表最后了,结束遍历。
2.2.2 跨度

跨度用于记录该层与前进节点连接的另一个节点的距离。

指向NULL的跨度为0.

如上图,可以清晰看出各个节点的各个层次的跨度。

跨度主要是用于计算排名rank的。当查找某个节点时,会通过前进指针进行查询,同时会将经过的所有层的跨度相加,就可以得到目标节点在跳跃表中的排名。

如上图,如果要查询对象o2,就要经过两个跨度为1的层,得到其排名为2。

2.3 分值和obj

节点属性中的score就是分值,在跳跃表中所有节点按照分值从小到大排序。

obj存储一个成员,为一个指针,指向一个字符串对象。

节点的成员必须唯一,但分值可以相同。分值相同的成员按字典中顺序排序。
如果一个集合中的所有成员都是整数值,则redis使用整数集合作为底层实现。

三、整数集合的实现

可保存类型为int16_t、int32_t或int64_t的整数值,且不会出现重复元素。

typedef struct intset{
    //编码
    uint32_t encoding;
    //元素数量
    uint32_t length;
    //元素数组
    int8_t contents[];
}intset;

contents[]数组保存一个整数集合中的所有元素,且按序存储。

数组的真正类型取决于encoding值。

四、升级

如果新添加的某个元素长度比其他几个都长,则所有元素都会升级到最长的这个元素的类型,如:-267738891737722773,1,3,4

虽然只有-267738891737722773是要用int64_t类型,其他三个只要int16_t就可以,但全部会升级为int64_t类型。

升级步骤:

  1. 根据新元素类型,扩展整个整数集合底层数组的空间大小,并为新元素分配控件
  2. 将底层数组所有元素转为与新元素相同的类型,并将转换类型后的元素放到正确的位置上,并保持有序性。
  3. 将新元素添加到数组的最后。

升级的好处

提升整数集合的灵活性

因为可以自动升级,所以可以随意的添加整数到集合,底层都会自动升级到合适的类型。

尽可能节约内存。

只有在需要升级的时候才会升级,如果所有元素都是一个类型,就不会进行升级。

四、降级

不支持降级,升级后会一直保持升级后的类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值