Redis跳跃表

本文摘抄自redis 源码剖析

跳跃表是一种随机化的数据,跳跃表以有序的方式在层次化的链表中保存元素,效率和平衡树媲美 ——查找、删除、添加等操作都可以在对数期望时间下完成,并且比起平衡树来说,跳跃表的实现要简单直观得多。
以下是个典型的跳跃表例子:


Redis跳跃表_第1张图片

从图中可以看到,跳跃表主要由以下部分构成:

  • 表头(head):负责维护跳跃表的节点指针。
  • 跳跃表节点:保存着元素值,以及多个层。
  • 层:保存着指向其他元素的指针。高层的指针越过的元素数量大于等于低层的指针,为了提高查找的效率,程序总是从高层先开始访问,然后随着元素值范围的缩小,慢慢降低层次。
  • 表尾:全部由NULL组成,表示跳跃表的末尾。

跳跃表的实现

为了满足自身的功能需要,Redis对跳跃表进行了如下修改:

  • 允许重复的score
  • 值:多个不同的member的score 值可以相同
    进行对比操作时,不仅要检查score值,还要检查member:当score
    值可以重复时,单靠score值无法判断一个元素的身份,所以需要连member域都一并检查才行。
    每个节点都带有一个高度为1层的后退指针用于从表尾方向向表头方向迭代:当执行ZREVRANGE或ZREVRANGEBYSCORE这类以逆序处理有序集的命令时,就会用到这个属性。

这个修改版的跳跃表由redis.h/zskiplist结构定义:

typedef struct zskiplist {
     // 头节点,尾节点 
    struct zskiplistNode *header, *tail;
   // 节点数量 
   unsigned long length;
   // 目前表内节点的最大层数 int level;
} zskiplist;

跳跃表的节点由redis.h/zskiplistNode定义:

typedef struct zskiplistNode { 
    //member 对象
    robj *obj;
   // 分值
   double score;
  // 后退指针
    struct zskiplistNode *backward;
  // 层 
  struct zskiplistLevel {
   // 前进指针
   struct zskiplistNode *forward;
  // 这个层跨越的节点数量
   unsigned int span; 
  } level[];
} zskiplistNode;

跳跃表的应用

和字典、链表或者字符串这几种在 Redis 中大量使用的数据结构不同,跳跃表在 Redis 的唯一作用,就是实现有序集数据类型

跳跃表将指向有序集的score值和member
域的指针作为元素,并以score 值为索引,对有序集元素进行排序。
举个例子,以下代码创建了一个带有 3 个元素的有序集:

redis> ZADD s 6 x 10 y 15 z
(integer) 3
redis> ZRANGE s 0 -1 WITHSCORES
1) "x"
2) "6"
3) "y"
4) "10"
5) "z"
6) "15"

在底层实现中,Redis 为x、y和z三个member分别创建了三个字符串,值分别为double类型的6、10和15,然后用跳跃表将这些指针有序地保存起来,形成这样一个跳跃表:



为了方便展示,在图片中我们直接将member和score值包含在表节点中,但是在实际的定义中,因为跳跃表要和另一个实现有序集的结构(字典)分享member和score值,所以跳跃表只保存指向member
和score的指针。

你可能感兴趣的:(Redis跳跃表)