返回

Redis源码剖析之Sorted Set有序集合

后端

Sorted Set 简介

Sorted Set 是一种有序集合,集合中的元素被分配了一个分数,并且元素按照分数的升序排列。Sorted Set 的特点是:

  • 元素是唯一的,即集合中不会出现重复的元素。
  • 元素可以被更新,但不能被删除。
  • 元素的分数可以被更新。
  • Sorted Set 支持快速查找、插入和删除操作,时间复杂度为 O(log n)。

Sorted Set 实现原理

Sorted Set 在 Redis 中是通过跳表(skip list)来实现的。跳表是一种随机化的数据结构,它将元素存储在多个层次中,每一层都有不同的概率被选择。这使得查找、插入和删除操作的时间复杂度都为 O(log n)。

跳表节点的结构定义如下:

typedef struct redisSkiplistNode {
    double score;
    robj *obj;
    struct redisSkiplistNode *backward;
    struct redisSkiplistLevel {
        unsigned long span;
        struct redisSkiplistNode *forward;
    } level[];
} redisSkiplistNode;

跳表可以被看作是一个有序链表的集合,其中每个链表都存储着不同分数的元素。链表之间的连接通过一个随机数来决定。

跳表的定义如下:

typedef struct redisSkiplist {
    struct redisSkiplistNode *header, *tail;
    unsigned int length;
    int level;
} redisSkiplist;

跳表节点查询的过程如下:

redisSkiplistNode *redisSkiplistFind(redisSkiplist *skiplist, double score) {
    redisSkiplistNode *node = skiplist->header;
    int i = skiplist->level - 1;

    while (i >= 0) {
        while (node->level[i].forward &&
               node->level[i].forward->score < score) {
            node = node->level[i].forward;
        }
        i--;
    }

    return node;
}

层数设置是跳表的关键,它决定了跳表的性能。层数越多,跳表的查找、插入和删除操作就越快,但同时跳表的内存占用也会越大。Redis 中的 Sorted Set 使用了自适应层数设置算法,它可以根据 Sorted Set 的大小自动调整层数。

跳表插入节点的过程如下:

void redisSkiplistInsert(redisSkiplist *skiplist, double score, robj *obj) {
    redisSkiplistNode *new_node = zslCreateNode(score, obj, NULL);
    redisSkiplistNode *update[REDIS_SKIPLIST_MAX_LEVEL];
    unsigned int rank[REDIS_SKIPLIST_MAX_LEVEL];
    int i, level;

    redisSkiplistNode *node = skiplist->header;
    for (i = skiplist->level - 1; i >= 0; i--) {
        while (node->level[i].forward &&
               node->level[i].forward->score < score) {
            rank[i] += node->level[i].span;
            node = node->level[i].forward;
        }
        update[i] = node;
    }

    level = redisGetRandomLevel();
    if (level > skiplist->level) {
        for (i = skiplist->level; i < level; i++) {
            update[i] = skiplist->header;
            rank[i] = 0;
            skiplist->level++;
        }
    }

    for (i = 0; i < level; i++) {
        new_node->level[i].span = skiplist->length - rank[i];
        new_node->level[i].forward = update[i]->level[i].forward;
        update[i]->level[i].forward = new_node;
    }

    skiplist->length++;
}

总结

Sorted Set 是 Redis 中一种非常重要的数据结构,它具有快速查找、插入和删除操作的特点。Sorted Set 在 Redis 中是通过跳表来实现的,跳表是一种随机化的数据结构,它将元素存储在多个层次中,每一层都有不同的概率被选择。这使得查找、插入和删除操作的时间复杂度都为 O(log n)。