Skip to content

Latest commit

 

History

History
67 lines (46 loc) · 4.25 KB

Redis-跳跃列表内部结构.md

File metadata and controls

67 lines (46 loc) · 4.25 KB

Redis-跳跃列表内部结构

  • Redis 的 zset 是一个复合结构,一方面它需要一个 hash 结构来存储 value 和 score 的 对应关系,另一方面需要提供按照 score 来排序的功能,还需要能够指定 score 的范围来获 取 value 列表的功能,这就需要另外一个结构「跳跃列表」。
  • zset 的内部实现是一个 hash 字典加一个跳跃列表 (skiplist)。

基本结构

image-20210909132101904

  • Redis 的跳跃表共有 64 层,意味着最 多可以容纳 2^64 次方个元素。每一个 kv 块对应的结构如下面的代码中的 zslnode 结构,kv header 也是这个结构,只不过 value 字段是 null 值——无效的,score 是 Double.MIN_VALUE,用来垫底的。
  • kv 之间使用指针串起来形成了双向链表结构,它们是 有序 排列的,从小到大。
  • 不同的 kv 层高可能不一样,层数越高的 kv 越少。同一层的 kv 会使用指针串起来。每一个层元素的遍历都是从 kv header 出发。
struct zslnode {
 string value;
 double score;
 zslnode*[] forwards; // 多层连接指针
 zslnode* backward; // 回溯指针
}
struct zsl {
 zslnode* header; // 跳跃列表头指针
 int maxLevel; // 跳跃列表当前的最高层
 map<string, zslnode*> ht; // hash 结构的所有键值对
}

查找过程

  • 跳跃列表查找算法复杂度O(lg(n))。

image-20210909133036201

  • 要定位到那个紫色的 kv,需要从 header 的最高层开始遍历找到第一个 节点 (最后一个比「我」小的元素),然后从这个节点开始降一层再遍历找到第二个节点 (最 后一个比「我」小的元素),然后一直降到最底层进行遍历就找到了期望的节点 (最底层的最 后一个比我「小」的元素)。
  • 将中间经过的一系列节点称之为「搜索路径」,它是从最高层一直到最底层的每一 层最后一个比「我」小的元素节点列表。

随机层数

  • 对于每一个新插入的节点,都需要调用一个随机算法给它分配一个合理的层数。
  • 官方的跳跃列表更加的扁平化,层高相对较低,在单个层上需要遍历的节点数量会稍多一 点。
  • 跳跃列表 会记录一下当前的最高层数 maxLevel,遍历时从这个 maxLevel 开始遍历性能就会提高很 多。

插入过程

  • 首先我们在搜索合适插入点的过程中将「搜索路径」摸出来了
  • 然后就可以开始创建新 节点了,创建的时候需要给这个节点随机分配一个层数,再将搜索路径上的节点和这个新节 点通过前向后向指针串起来
  • 如果分配的新节点的高度高于当前跳跃列表的最大高度,就需 要更新一下跳跃列表的最大高度

删除过程

  • 删除过程和插入过程类似,都需先把这个「搜索路径」找出来。然后对于每个层的相关 节点都重排一下前向后向指针就可以了。同时还要注意更新一下最高层数 maxLevel。

更新过程

  • 如果这个 value 已经存在了,只是调整一下 score 的值,那就需要走一个更新的流程。假设这个新的 score 值不会带来排序位置上的改变,那么就不需要调整位置,直接修改元素的 score 值就 可以了
  • 如果排序位置变了,Redis是先删除这个元素,再插入这个元素

如果score值都一样呢

  • zset 的排序元素不只看 score 值,如果 score 值相同还需要再比较 value 值 (字符串比较)

元素排名是怎么算出来的

  • zset 可以获取元素的排 名 rank
  • 那这个 rank 是如何算出来的?
    • Redis 在 skiplist 的 forward 指针上进行了优化,给每一个 forward 指针都增加了 span 属 性,span 是「跨度」的意思,表示从前一个节点沿着当前层的 forward 指针跳到当前这个节 点中间会跳过多少个节点。Redis 在插入删除操作时会小心翼翼地更新 span 值的大小。
    • 当我们要计算一个元素的排名时,只需要将「搜索路径」上的经过的所有节点的跨 度 span 值进行叠加就可以算出元素的最终 rank 值。