redis skiplist (跳跃表)
redis skiplist (跳跃表)
概述
redis skiplist 是有序的, 按照分值大小排序
节点中存储多个指向其他节点的指针
结构
- zskiplist 结构
// 跳跃表
typedef struct zskiplist {
// 表头节点和表尾节点
struct zskiplistNode *header, *tail;
// 表中节点的数量 (不包括表头结点)
unsigned long length;
// 表中层数最大的节点的层数 (不包括表头结点)
int level;
} zskiplist;
- length, level 属性不包含表头结点, 代码如下:
// 创建并返回一个新的跳跃表
zskiplist *zslCreate(void) {
int j;
zskiplist *zsl;
// 分配空间
zsl = zmalloc(sizeof(*zsl));
// 设置高度和起始层数
zsl->level = 1;
zsl->length = 0;
// 初始化表头节点
zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL);
for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {
zsl->header->level[j].forward = NULL;
zsl->header->level[j].span = 0;
}
zsl->header->backward = NULL;
// 设置表尾
zsl->tail = NULL;
return zsl;
}
- 由上代码可见, 初始化的表头结点, level 为 ZSKIPLIST_MAXLEVEL = 32, 但是 zsl->level = 1, zsl->length = 0, 说明这两个属性并没有将表头结点包含在内
- zskiplistNode 结构 (节点)
// 节点
typedef struct zskiplistNode {
// 成员对象
robj *obj;
// 分值
double score;
// 后退指针, 指向前置节点
struct zskiplistNode *backward;
// 层
struct zskiplistLevel {
// 前进指针
struct zskiplistNode *forward;
// 跨度
unsigned int span;
} level[];
} zskiplistNode;
- level 层
struct zskiplistLevel {
// 前进指针
struct zskiplistNode *forward;
// 跨度
unsigned int span;
};
节点中的 level 层结构中的 forward 指针只能指向同 level 层的节点
span 是记录相邻层之间的跨度, 在利用排位获取节点时使用, 相对于直接遍历表, 效率更高
其他
- range 表示, 使用 zrangespec 结构
// 表示开区间/闭区间范围的结构
typedef struct {
// 最小值和最大值
double min, max;
// 指示开区间还是闭区间
// 值为 1 表示开,值为 0 表示闭
int minex, maxex;
} zrangespec;
总结
skiplist 是有序的, 由小到大
skiplist 在普通列表基础上增加了 level 层和 score 概念
level 层是在创建节点时随机生成的层数
为了满足根据排名查询数据的需要, 避免遍历表去查找, 设置了 span 跨度参数
每个节点的 backward 参数都指向前置节点
头节点可以抽象的理解为不属于 skiplist, 因为它不属于 skiplist 的长度和最大层数, 头节点的 level 固定为 32
skiplist api (以 zset 为例 src/t_zset.c)
函数 | 作用 | 备注 |
zslCreateNode | 创建 level 层, 分值为 score, 对象为 obj 的跳跃表节点 | zskiplistNode *zslCreateNode(int level, double score, robj *obj) |
zslCreate | 创建并返回一个新的跳跃表 | zskiplist *zslCreate(void) |
zslFreeNode | 释放跳跃表节点 | void zslFreeNode(zskiplistNode *node) |
zslFree | 释放跳跃表 | void zslFree(zskiplist *zsl) |
zslRandomLevel | 生成一个随机数作为节点的层数 | int zslRandomLevel(void) |
zslInsert | 创建一个对象为 obj, 分值为 score 的跳跃表节点, 并将其插入跳跃表 zsl 中 | zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj) |
zslDeleteNode | 从 zsl 中删除指定节点 x, 并更新有关节点 update 的信息 | void zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) |
zslDelete | 从 zsl 中删除指定分值 score, 指定对象 obj 的节点 | int zslDelete(zskiplist *zsl, double score, robj *obj) |
zslValueGteMin | 检测给定的 value 值是否大于等于 spec 的 min 值 | static int zslValueGteMin(double value, zrangespec *spec) |
zslValueLteMax | 检测给定的 value 值是否小于等于 spec 的 max 值 | static int zslValueLteMax(double value, zrangespec *spec) |
zslIsInRange | 检测给定的 range 值是否在 zsl 范围内 | int zslIsInRange(zskiplist *zsl, zrangespec *range) |
zslFirstInRange | 返回 zsl 中首个在范围中的节点 | zskiplistNode *zslFirstInRange(zskiplist *zsl, zrangespec *range) |
zslLastInRange | 返回 zsl 中最后一个在范围内的节点 | zskiplistNode *zslLastInRange(zskiplist *zsl, zrangespec *range) |
zslDeleteRangeByScore | 删除给定 range 范围的 zsl 节点 | unsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec *range, dict *dict) |
zslDeleteRangeByRank | 根据 rank 排名范围获取所有节点 | unsigned long zslDeleteRangeByRank(zskiplist *zsl, unsigned int start, unsigned int end, dict *dict) |
zslGetRank | 获取给定 score 和 robj 的节点在表中的排位 | unsigned long zslGetRank(zskiplist *zsl, double score, robj *o) |
zslGetElementByRank | 根据 rank 排位获取节点 | zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) |
zslParseRange | 解析 min, max, 将其存入 spec | static int zslParseRange(robj *min, robj *max, zrangespec *spec) |