redis-zset数据结构探索

Posted ginkgo-leaf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis-zset数据结构探索相关的知识,希望对你有一定的参考价值。

redis用的人比较多,其中zset大家都熟悉,主要用于排名场景。
zset数据结构,分成两部分,一部分是用于排序,一部分用于缓存键值。
先看看结构:

typedef struct zset {
    dict *dict;         //缓存
    zskiplist *zsl;     //排序结构
} zset;

 

上面,跳跃表用于排序结构,可以按照名次,积分查找对应键, 时间复杂度: log(n)。
按照名次,积分范围查找一系列键时, 先查询满足条件的第一个键,然后当前键查找后续键, 时间复杂度: log(n) + o(m), n=总键数, m=查询结果键数。

跳跃表结构:

typedef struct zskiplist {
    struct zskiplistNode *header, *tail;        //结点头:用于顺序查询,常用方式; 结点尾:用于倒序简单查询。
    unsigned long length;                       //结点数
    int level;                                  //跳跃层级
} zskiplist;

 

结点结构:

typedef struct zskiplistNode {
    robj *obj;                                  //
    double score;                               //积分
    struct zskiplistNode *backward;             //前一个结点, 和level[0]可看作双链表
    struct zskiplistLevel {                     //跳跃层关系, 每层都是单链表
        struct zskiplistNode *forward;          //此层下一个结点
        unsigned int span;                      //此层下一个结点和当前结点距离(两者隔了多少结点)
    } level[];                                  //最多32层
} zskiplistNode; 


查询:
根据名次范围查询

void zrangeGenericCommand(client *c, int reverse) {
        ......

        zset *zs = zobj->ptr;           //zset结构变量
        zskiplist *zsl = zs->zsl;       //跳跃表
        zskiplistNode *ln;
        robj *ele;

        /* Check if starting point is trivial, before doing log(N) lookup. */
        if (reverse) {                  //是否倒序查询
            ln = zsl->tail;             //默认取尾结点
            if (start > 0)
                ln = zslGetElementByRank(zsl,llen-start);       //如果start>0, 则取对应结点
        } else {
            ln = zsl->header->level[0].forward; //默认取第一个结点
            if (start > 0)
                ln = zslGetElementByRank(zsl,start+1);
        }

        while(rangelen--) {             //取rangelen个结点
            serverAssertWithInfo(c,zobj,ln != NULL);
            ele = ln->obj;
            addReplyBulk(c,ele);        //响应键名
            if (withscores)
                addReplyDouble(c,ln->score);    //响应键值
            ln = reverse ? ln->backward : ln->level[0].forward; //设置下一个结点
        }

        ......
}

/* Finds an element by its rank. The rank argument needs to be 1-based. */
zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) {
    zskiplistNode *x;
    unsigned long traversed = 0;                //当前名次
    int i;

    x = zsl->header;                            //头结点, 从头结点的下一个结点遍历
    for (i = zsl->level-1; i >= 0; i--) {       //从高层到低层链表遍历
        while (x->level[i].forward && (traversed + x->level[i].span) <= rank)   //如果有下一个结点,且下一个结点的名次<=rank
        {
            traversed += x->level[i].span;
            x = x->level[i].forward;
        }
        if (traversed == rank) {                //找到对应名次的结点
            return x;
        }
    }
    return NULL;
}

 

zslGetElementByRank()时间复杂度理想值 = log(n)

 

...








以上是关于redis-zset数据结构探索的主要内容,如果未能解决你的问题,请参考以下文章

Redis-ZSet常用命令

DOM探索之基础详解——学习笔记

如何利用常见的文本挖掘方法去探索分子数据集?

你知道的Go切片扩容机制可能是错的

将vscode打造成无敌的IDE初步探索vscode,解放生产力

VSCode自定义代码片段5——HTML元素结构