redis学习笔记之数据结构-----skiplist(跳跃表)

概述

组成结构

操作

查询

插入

分配层数

删除

更新

排名

推荐阅读


概述

redis有序集合它可以需要按照提供的score来进行按顺序获取value列表,也需要一个hash结构来存储value和score的对应关系,为了实现这两个功能,redis对有序集合底层使用了跳跃表来实现。如果你对跳跃表没有了解,建议先去看看小灰讲解跳跃表

组成结构

struct zslnode{

    string value;

    double score;

    //多层连接指针
    zslnode*[] forwards;

    //回溯指针
    zslnode*[] backward;
}

struct zsl{
    //头指针
    zslnode* header;

    //跳跃表最高层
    int maxlevel;
    
    //所有键值对
    map<string, zslnode*> ht;
}

操作

查询

如上图所示,假设我们要搜索17,我们先从最左边的header开始,从最高层(l3)开始我们先遇到一个节点21,我们要找的比他小所以往下面一层(l2)发现左边的节点2和右边的节点37不满足条件,我们接着来到下一层(l1)发现左边的节点就是我们的数据,取出然后返回

插入

插入我们先要找到适合的位置,然后开始创建新的节点,创建的时候我们需要随机分配一个层数(如果分配的层数高于最高层数,那么需要更新跳跃表的最高高度),新节点要将前后两个节点串起来

分配层数

没插入一个新节点都需要分配一个层数,理论上来说往上一层的概率为50%,最高层数限制64层,所以到64层的概率为2^-63。下面看下随机函数

//本代码来自redis深度历险
//新节点随机层数

int zslRandomLevel(void){

    int level = 1;
    //ZSKIPLIST_P在redis源码中晋升率为25%
    while (random() & 0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
        level+= 1;
    return (level < ZSKIPLIST_MAXLEVEL) ? level :ZSKIPLIST_MAXLEVEL;
}

删除

删除时候我们需要找到删除的节点的位置,删除后被删除节点的前后节点需要重排(这两个节点连接),如果影响到了最高层数,则需要修改下maxLevel

更新

当我们调用zadd时候,如果数据不存在则进行插入操作,如果存在则需要修改下这个节点的score。修改操作需要进行两次查询,第一次查询我们需要找到对应的节点进行删除,第二次查询适合位置进行插入操作。在更新中还有一种特殊情况,有序集合中所有的score相同,那么还需要比较下value(字符串比较)

排名

//代码来自redis深度历险
struct zslforward{

    zslnode* item;
    
    //跨度
    long span;
}

在我们正常使用有序集合,它是排序的,那么这个排序它实现的?在forward指针汇总,有一个span字段记录着当前层的当前节点沿着forward指针跳跃到下一节点需要经历多少个节点

推荐阅读

为啥 redis 使用跳表(skiplist)而不是使用 red-black?

文来自于redis深度历险,redis设计与实现redis官方文档等待总结出来的,有什么不足请谅解,最后还是大家有空去去读下钱文品的redis深度历险这本书很给力的。

猜你喜欢

转载自blog.csdn.net/lin_keys/article/details/105891687