Redis底层数据结构之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 之类的操作,取出一片连续的节点。这些操作的缓存命中率不会比红黑树低。

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值