当前位置:   article > 正文

Redis底层数据结构之skipList(跳跃表)_skiplist查找数据的过程

skiplist查找数据的过程

关于跳跃表其实在 JUC 里面有一个并发容器就是利用跳跃表来实现的:ConcurrentSkipListMap。jdk的并发包中还有许多数据结构,最常用的又链表,哈希表等。

先介绍一下跳表的相关特性:

  • 由很多层结构组成,level是通过一定的概率随机产生的(使用抛硬币的方式)
  • 每一层都是一个有序的链表,默认是升序,也可以根据创建映射时所提供的Comparator进行排序,具体取决于使用的构造方法
  • 最底层(Level 1)的链表包含所有元素
  • 如果一个元素出现在Level i 的链表中,则它在Level i 之下的链表也都会出现
  • 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素

这些特性我们看到跳表的结构就很容易理解了:结构如下图:

 

 最底层的是原链表,然后一级索引,二级索引,二级索引为什么选择1这个结点作为关键结点呢,是通过概率产生的,就是抛硬币,正反的概率都是百分之50,虽然这个图是均匀间隔的,但是实际上每一个节点都有可能成为关键节点。二级索引的关键节点也是这么产生的。

skiplist的查找

SkipListd的查找算法较为简单,对于上面我们我们要查找元素3,其过程如下:

  1. 比较1,大于,往后找(6),然后就到下一层
  2. 比6小,继续往前找(3)
  3. 找到3(加入没找到,就继续到下一层开始找)

skiplist的插入

SkipList的插入操作主要包括:

  1. 查找合适的位置。这里需要明确一点就是在确认新节点要占据的层次K时,采用丢硬币的方式,完全随机。如果占据的层次K大于链表的层次,则重新申请新的层,否则插入指定层次
  2. 申请新的节点
  3. 调整指针

假定我们要插入的元素为5,其过程如下:

1.比较1,大于,往后找(6),小于,到下一层

2.比较3,大于,往后找(6),小于,到下一层,也就是原链表,确定位置为4后6前,插入到原链表

3.然后决定在一级索引上是不是关键节点,是,就继续抛,到二级索引,如果还是就继续抛,申请新的层

4.如果抛出来否,就不继续抛了

skiplist的删除

删除节点和插入节点思路基本一致:找到节点,删除节点,调整指针。

redis的skipList和经典的skiplist做了些许改动,有兴趣的自己查看源码

为什么不用红黑树作为zset底层实现?

相比较红黑树来讲作者觉得这个跳表的实现更加简单,后期维护也更加方便:

  • 缺点:
    • 比红黑树占用更多的内存,每个节点的大小取决于该节点的层数
    • 空间局部性较差导致缓存命中率低,感觉上会比红黑树更慢
  • 优点:
    • 实现比红黑树简单
    • 比红黑树更容易扩展,作者之后实现 zrank 指令时没怎么改动代码。
    • 红黑树插入删除时为了平衡高度需要旋转附近节点,高并发时需要锁。skiplist 不需要考虑。跳表维持结构平衡的成本比较低,完全靠随机。
    • 一般用 zset的 操作都是执行 zrange 之类的操作,取出一片连续的节点。这些操作的缓存命中率不会比红黑树低。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/756230
推荐阅读
相关标签
  

闽ICP备14008679号