看完Redis源码还不理解跳跃表吗?( 五 )


但通过使用一个 zskiplist 结构来持有这些节点,程序可以更方便地对整个跳跃表进行处理,比如快速访问跳跃表的表头节点和表尾节点,又或者快速地获取跳跃表节点的数量(也即是跳跃表的长度)等信息,如下所示:

看完Redis源码还不理解跳跃表吗?

文章插图
 
zskiplist 结构的定义如下:
typedef struct zskiplist {    // 表头节点和表尾节点    struct zskiplistNode *header, *tail;    // 表中节点的数量    unsigned long length;    // 表中层数最大的节点的层数    int level;} zskiplist;
  • header 和 tail 指针分别指向跳跃表的表头和表尾节点,通过这两个指针,程序定位表头节点和表尾节点的复杂度为 O(1)。
  • 通过使用 length 属性来记录节点的数量,程序可以在 O(1) 复杂度内返回跳跃表的长度 。
  • level 属性则用于在 O(1) 复杂度内获取跳跃表中层高最大的那个节点的层数量,注意表头节点的层高并不计算在内 。
跳跃表API列出了跳跃表的所有操作 API。
看完Redis源码还不理解跳跃表吗?

文章插图
 
面试:为啥 redis 使用跳表(skiplist)而不是使用 red-black?
  1. skiplist的复杂度和红黑树一样,而且实现起来更简单 。
  2. 在并发环境下skiplist有另外一个优势,红黑树在插入和删除的时候可能需要做一些rebalance的操作,这样的操作可能会涉及到整个树的其他部分,而skiplist的操作显然更加局部性一些,所需要盯住的节点更少,因此在这样的情况下性能好一些 。
 
附:开发者说的为什么选用skiplist The Skip list
看完Redis源码还不理解跳跃表吗?

文章插图
 
总结
  • 跳跃表是有序集合的底层实现之一,除此之外它在 Redis 中没有其他应用 。
  • Redis 的跳跃表实现由 zskiplist 和 zskiplistNode 两个结构组成,其中 zskiplist 用于保存跳跃表信息(比如表头节点、表尾节点、长度),而 zskiplistNode 则用于表示跳跃表节点 。
  • 每个跳跃表节点的层高都是 1 至 32 之间的随机数 。
  • 在同一个跳跃表中,多个节点可以包含相同的分值,但每个节点的成员对象必须是唯一的 。
  • 跳跃表中的节点按照分值大小进行排序,当分值相同时,节点按照成员对象的大小进行排序 。
参考资料跳跃表:
http://redisbook.com/preview/skiplist/datastruct.html
 
文章持续更新,可以公众号搜一搜「 一角钱技术 」第一时间阅读,本文 GitHub org_hejianhui/JAVAStudy 已经收录,欢迎 Star 。




推荐阅读