【总结】跳跃表

该文章为知识总结的文章,如果是初学者,建议先从专栏学习:Redis专栏

一、为什么要使用跳跃表?

Zset要支持随机的插入和删除,所以它 不宜使用数组来实现,关于排序问题,我们也很容易就想到 红黑树/ 平衡树 这样的树形结构,为什么 Redis 不使用这样一些结构呢?

  • 性能考虑: 在高并发的情况下,树形结构需要执行一些类似于 rebalance 这样的可能涉及整棵树的操作,相对来说跳跃表的变化只涉及局部
  • 实现考虑: 在复杂度与红黑树相同的情况下,跳跃表实现起来更简单,看起来也更加直观;
  • 大小考虑:平衡树结构更复杂,占的空间更多,而跳跃表相对简单

二、什么是跳跃表?

对于一个普通链表,需要这个链表按照 score 值进行排序,这也就意味着,当我们需要添加新的元素时,我们需要定位到插入点,这样才可以继续保证链表是有序的,通常我们会使用 二分查找法,但二分查找是有序数组的,链表没办法进行位置定位,我们除了遍历整个找到第一个比给定数据大的节点为止 (时间复杂度 O(n)) 似乎没有更好的办法。

假如我们每相邻两个节点之间就增加一个指针,让指针指向下下一个节点,如图所示

img

  • 这样所有新增的指针连成了一个新的链表,但它包含的数据却只有原来的一半

现在假设我们想要查找数据时,可以根据这条新的链表查找,如果碰到比待查找数据大的节点时,再回到原来的链表中进行查找,比如,我们想要查找 7,查找的路径则是沿着下图中标注出的红色指针所指向的方向进行的:

  • 通过新增加的指针查找,我们不再需要与链表上的每一个节点逐一进行比较,这样改进之后需要比较的节点数大概只有原来的一半

利用同样的方式,我们可以在新产生的链表上,继续为每两个相邻的节点增加一个指针,从而产生第三层链表:

  • 在这个新的三层链表结构中,我们试着 查找 13,那么沿着最上层链表首先比较的是 11,发现 11 比 13 小,于是我们就知道只需要到 11 后面继续查找,从而一下子跳过了 11 前面的所有节点。

可以想象,当链表足够长,这样的多层链表结构可以帮助我们跳过很多下层节点,从而加快查找的效率,这样查找过程就非常类似于一个二分查找,使得查找的时间复杂度可以降低到 O(logn)。

但是,这种方法在插入数据的时候有很大的问题。新插入一个节点之后,就会打乱上下相邻两层链表上节点个数严格的 2:1 的对应关系。如果要维持这种对应关系,就必须把新插入的节点后面的所有节点 (也包括新插入的节点) 重新进行调整,这会让时间复杂度重新蜕化成 O(n)。删除数据也有同样的问题。

为了避免这一问题,不要求上下相邻两层链表之间的节点个数有严格的对应关系,而是 为每个节点随机出一个层数(level)。比如,一个节点随机出的层数是 3,那么就把它链入到第 1 层到第 3 层这三层链表中。

从上面的创建和插入的过程中可以看出,每一个节点的层数(level)是随机出来的,而且新插入一个节点并不会影响到其他节点的层数,因此,插入操作只需要修改节点前后的指针,而不需要对多个节点都进行调整,这就降低了插入操作的复杂度。

现在我们假设从我们刚才创建的这个结构中查找 23 这个不存在的数,那么查找路径会如下图:

三、Redis实现

  • 随机层数:对于每一个新插入的节点,都需要调用一个随机算法给它分配一个合理的层数,Redis 跳跃表默认允许最大的层数是 32
  • 插入节点实现:1.找到当前我需要插入的位置 ,其中包括相同 score 时的处理,如果值相同,则比较value值,是一个字符串的比较;2.创建新节点,调整前后的指针指向,完成插入
  • 元素排名的实现:跳跃表本身是有序的,Redis 在 skiplist 的 forward 指针上进行了优化,给每一个 forward 指针都增加了 span 属性,用来表示从前一个节点沿着当前层的 forward 指针跳到当前这个节点中间会跳过多少个节点。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在SQL Server 2012中,出现id跳跃的情况可能是由于系统重启、备份/还原数据库、删除数据行或插入失败等原因引起的。这是由于SQL Server的自增列机制的特点导致的。当发生这些情况时,自增列的值可能会跳过某些数字。 具体来说,在SQL Server的自增列中,每次插入新行时,自增列的值会自动增加1。然而,当发生上述情况时,系统会将自增列的当前值保存在系统表中,并在下一次插入时使用该值作为新行的自增列值。因此,如果发生了系统重启或其他操作,自增列的值可能会跳过之前的值。 解决这个问题的方法有两种: 1. 使用DBCC CHECKIDENT命令来重新设置自增列的当前值。例如,可以使用以下命令将自增列的当前值设置为最大值+1: DBCC CHECKIDENT ('YourTableName', RESEED) 2. 另一种解决方法是使用IDENTITY_INSERT选项来插入指定的自增列值。这样可以手动控制自增列的值,避免跳跃。但是需要注意的是,使用IDENTITY_INSERT选项需要在插入数据前启用,并且只能对单个表进行操作。 需要注意的是,跳跃的自增列值并不会对数据库的功能或数据完整性造成影响,只是在视觉上可能不连续。因此,如果不是特别需要连续的自增列值,可以不予理会。 总结一下,当在SQL Server 2012中出现id跳跃的情况时,这可能是由于系统重启、备份/还原数据库、删除数据行或插入失败等原因引起的。可以通过使用DBCC CHECKIDENT命令重新设置自增列的当前值或使用IDENTITY_INSERT选项来解决这个问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值