分类:Redis| 发布时间:2019-05-11 00:48:00
本文是 Redis 5 数据结构系列的第七篇,主要讲解 skiplist 的结构和实现细节。
本文不会讲解 skiplist 本身的定义和算法细节,如果没接触过 skiplist 的可以先看看 skiplist 原理与实现。
skiplist 的源码在 src/server.h 和 src/t_zset.c。
Redis 的 skiplist 基本就是 William Pugh in 论文:"Skip Lists: A Probabilistic Alternative to Balanced Trees" 里面描述的 skiplist 的 C 语言实现版本。 Redis 的 skiplist 在论文描述的实现的基本上做了如下改变:
skiplist 结构的定义如下:
typedef struct zskiplistNode {
sds ele;
double score;
struct zskiplistNode *backward;
struct zskiplistLevel {
struct zskiplistNode *forward;
unsigned long span;
} level[];
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
} zskiplist;
和系列文章前几篇介绍的不同。 Redis 的 skiplist 总是和哈希表一起组合使用,用来表示 Redis 中的有序集合对象, 因此 skiplist 的实现代码并没有使用单独和源文件,而是和有序集合相关代码一起放在了 src/t_zset.c 中。
下图给出了一个 Redis 实现的 skiplist 的示例:
zskiplist *zslCreate(void);
创建一个 skiplist
void zslFree(zskiplist *zsl);
释放一个 skiplist
zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele);
插入一个 skiplist 节点。假设 skiplist 不存在此节点(由调用者保证)。 sds 的所有权转移到 skiplist 中。
返回插入的节点。
int zslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node);
删除 skiplist 中和传入的 score 与 element 匹配的节点。 若 skiplist 中找到指定的节点则返回 1,否则返回 0。
如果 'node' 为 NULL 则会使用 zslFreeNode 删除节点,否则将节点赋值给 *node 指针(匹配的节点只会从 skiplist 中 unlink)。
typedef struct {
double min, max;
int minex, maxex; /* are min or max exclusive? */
} zrangespec;
zskiplistNode *zslFirstInRange(zskiplist *zsl, zrangespec *range);
zskiplistNode *zslLastInRange(zskiplist *zsl, zrangespec *range);
unsigned long zslGetRank(zskiplist *zsl, double score, sds o);
返回指定节点的 rank 值,返回 0 表示未找到指定的节点。
注意: rank 表示的是 zsl->header 和指定节点的距离,因此所有有效的节点总是大于等于 1。
int zslValueGteMin(double value, zrangespec *spec) {
return spec->minex ? (value > spec->min) : (value >= spec->min);
}
int zslValueLteMax(double value, zrangespec *spec) {
return spec->maxex ? (value < spec->max) : (value <= spec->max);
}
这两个方法比较简单,直接把实现贴出来了
#define ZSKIPLIST_MAXLEVEL 64 /* Should be enough for 2^64 elements */
#define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */