在 Redis 中,跳跃表(Skiplist)主要用于实现有序集合(Sorted Set)数据结构。有序集合在 Redis 中的命令前缀为 Z,例如 ZRANGEZADDZREM 等。有序集合中的元素是按分数(score)排序的,而跳跃表是一种高效的数据结构,可以支持快速的插入、删除和按分数排序的查找操作。

跳跃表的用途

跳跃表是一种概率平衡的数据结构,提供了类似于平衡树的时间复杂度。它被用来实现 Redis 中的有序集合,主要用于以下场景:

  1. 有序数据的快速查找:跳跃表允许快速按分数查找元素。例如,获取某个分数范围内的所有元素或查找某个分数对应的元素。
  2. 范围查询:跳跃表支持高效的范围查询,可以快速获取分数在某个范围内的所有元素。
  3. 排序操作:跳跃表保持元素按分数排序,支持按分数的升序或降序遍历。

主要实现文件

在 Redis 源代码中,跳跃表的实现主要集中在 zskiplist.c 文件中。该文件定义了跳跃表的数据结构及其相关操作。

跳跃表的数据结构

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

typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length;
    int level;
} zskiplist;

主要操作

  1. 插入操作:向跳跃表中插入一个元素,同时按照分数(score)排序。

    zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj);
  2. 删除操作:从跳跃表中删除一个元素。

    int zslDelete(zskiplist *zsl, double score, robj *obj);
  3. 范围查找:查找分数在某个范围内的元素。

    unsigned long zslGetRank(zskiplist *zsl, double score, robj *o);
    zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank);
  4. 范围删除:删除分数在某个范围内的所有元素。

    unsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec *range);
    unsigned long zslDeleteRangeByRank(zskiplist *zsl, unsigned int start, unsigned int end);

应用示例

假设有一个有序集合存储了学生的成绩,使用跳跃表可以实现以下操作:

  • 插入学生成绩ZADD students 95 "Alice" 将 Alice 的成绩 95 插入到有序集合 students 中。
  • 获取某个分数范围内的学生ZRANGE students 0 100 WITHSCORES 获取成绩在 0 到 100 之间的所有学生及其成绩。
  • 删除某个学生的成绩ZREM students "Alice" 从有序集合中删除 Alice 的成绩。

通过使用跳跃表,Redis 实现了高效的有序集合操作,提供了灵活而强大的数据查询和操作能力。