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)

redis skiplist (跳跃表)的更多相关文章

  1. 小白也能看懂的Redis教学基础篇——朋友面试被Skiplist跳跃表拦住了

    各位看官大大们,双节快乐 !!! 这是本系列博客的第二篇,主要讲的是Redis基础数据结构中ZSet(有序集合)底层实现之一的Skiplist跳跃表. 不知道那些是Redis基础数据结构的看官们,可以 ...

  2. Redis数据结构—跳跃表

    目录 Redis数据结构-跳跃表 跳跃表产生的背景 跳跃表的结构 利用跳跃表查询有序链表 Redis跳跃表图示 Redis跳跃表数据结构 小结 Redis数据结构-跳跃表 大家好,我是白泽,最近学校有 ...

  3. 【Redis】skiplist跳跃表

    有序集合Sorted Set zadd zadd用于向集合中添加元素并且可以设置分值,比如添加三门编程语言,分值分别为1.2.3: 127.0.0.1:6379> zadd language 1 ...

  4. Redis(2)——跳跃表

    一.跳跃表简介 跳跃表(skiplist)是一种随机化的数据结构,由 William Pugh 在论文<Skip lists: a probabilistic alternative to ba ...

  5. 【Redis】跳跃表原理分析与基本代码实现(java)

    最近开始看Redis设计原理,碰到一个从未遇见的数据结构:跳跃表(skiplist).于是花时间学习了跳表的原理,并用java对其实现. 主要参考以下两本书: <Redis设计与实现>跳表 ...

  6. 浅析SkipList跳跃表原理及代码实现

    本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈“跳跃表”的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代 ...

  7. 【转】浅析SkipList跳跃表原理及代码实现

    SkipList在Leveldb以及lucence中都广为使用,是比较高效的数据结构.由于它的代码以及原理实现的简单性,更为人们所接受.首先看看SkipList的定义,为什么叫跳跃表? "S ...

  8. redis的跳跃表

    跳跃表是一种插入.查询.删除的平均时间复杂度为O(nlogn)的数据结构,在最差情况下是O(n),当然这几乎很难出现. 和红黑树相比较 最差时间复杂度要差很多,红黑树是O(nlogn),而跳跃表是O( ...

  9. SkipList 跳跃表

    引子 考虑一个有序表:14->->34->->50->->66->72 从该有序表中搜索元素 < 23, 43, 59 > ,需要比较的次数分别为 ...

随机推荐

  1. Vuex 学习总结

    好在之前接触过 flux,对于理解 vuex 还是很有帮助的.react 学到一半,后来因为太忙,就放弃了,现在也差不多都忘记了.不过感觉 vuex 还是跟 flux 还是有点区别的. 对于很多新手来 ...

  2. 中软卓越IT培训:给IT程序员的18个忠告

    1 .想清楚,写清楚,说清楚,才是真正的清楚! 2 .多花点时间沟通清楚需求,才能把握正确方向! 3 .修复需求错误的成本是代码错误的几十倍! 4 .程序员最大的坏习惯就是:急于动手写代码! 5 .提 ...

  3. java装箱跟拆箱解析

    /** * 在jdk1.5之后,java为基本数据类型到对应的应用数据类型提供了自动拆箱装箱操作 * 不管是自动拆箱还是自动装箱都是应用数据类型有的方法,基本数据类型是没有任何方法可调用的 *从概念上 ...

  4. 1059: [ZJOI2007]矩阵游戏

    1059: [ZJOI2007]矩阵游戏 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2154  Solved: 1053[Submit][Stat ...

  5. 简易漫画网站搭建-漫画喵Server版

    小喵的唠叨话:寒假的时候写了一个漫画爬虫,爬取了好几个漫画,不过一直没有找到合适的漫画阅读的工具.因此最近就试着自己写一个漫画的网站,放在公网上或者局域网里,这样就能随时随地用手机.Pad看漫画了. ...

  6. Vue学习之路---No.6(分享心得,欢迎批评指正)

    我们还是先回顾一下上一次的重点: 1.事件绑定,我们可以对分别用方法和js表达式对事件进行处理 2.当方法名带括号的时候,在方法中一定要传参:而不带括号的时候,vm会自动配置默认event 3.各类事 ...

  7. 关于<context:property-placeholder>的一个有趣现象

    转:http://stamen.iteye.com/blog/1926166 先来看下A和B两个模块 A模块和B模块都分别拥有自己的Spring XML配置,并分别拥有自己的配置文件: A模块 A模块 ...

  8. PHP生成随机水印图片

    基于PHP的GD图形库,自己生成一张图片.仅限初识GD库,实例学习. 一.需求 网站的布局用到了类似慕课网课程列表的风格,每一个课程是一个banner图,图下面是标题加简介.因为课程的数量较大没有为所 ...

  9. 【Java基础】通过getResourceAsStream() 加载资源文件

    Class.getResourceAsStream(String path) path不以"/"开头时,默认是从当前类所在的包下面获取资源 path以"/"开头 ...

  10. echarts柱图自定义为硬币堆叠的形式

    看这标题,可能会有一些人不太明白,那么直接上图,就是柱图展示形式如下图(兼容IE8) 要想实现这样展示效果.我们想用echarts直接实现不行的,即使是纹理填充也不可行的,但是我们可以借助echart ...