Redisbook学习笔记(1)跳跃表

一个典型的跳跃表例子

wKioL1LhEg7jjdduAACBHTnczTk625.jpg

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

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

为了适应自身的功能需要,Redis 基于William Pugh 论文中描述的跳跃表进行了以下修改:
1. 允许重复的score 值:多个不同的member 的score 值可以相同。
2. 进行对比操作时,不仅要检查score 值,还要检查member :当score 值可以重复时,
单靠score 值无法判断一个元素的身份,所以需要连member 域都一并检查才行。
3. 每个节点都带有一个高度为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 分别创建了三个字符串,并为6 、10 和15
分别创建三个double 类型的值,然后用一个跳跃表将这些指针有序地保存起来,形成这样一
个跳跃表:

wKiom1LhFAjBgp6ZAAC90-4n6xM890.jpg

小结
 跳跃表是一种随机化数据结构,它的查找、添加、删除操作都可以在对数期望时间下完
成。
 跳跃表目前在Redis 的唯一作用就是作为有序集类型的底层数据结构(之一,另一个构
成有序集的结构是字典)。
 为了适应自身的需求,Redis 基于William Pugh 论文中描述的跳跃表进行了修改,包括:
1. score 值可重复。
2. 对比一个元素需要同时检查它的score 和memeber 。
3. 每个节点带有高度为1 层的后退指针,用于从表尾方向向表头方向迭代。

 

 

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