Redis跳跃表

Posted 原创工厂

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis跳跃表相关的知识,希望对你有一定的参考价值。

Redis的有序集合ZSet是通过跳跃表实现的,那么什么是跳跃表?为何要采用跳跃表作为ZSet的数据结构,其性能如何?

Redis跳跃表数据结构
跳跃表由zskiplist和zskiplistNode组成。每个跳跃表节点的层数都是1-32之间的随机数(每创建一个节点,程序会随机生成一个[1-32]作为level数组的大小)。
同一个跳跃表中,多个节点可以包含相同的分值,但节点的成员对象是唯一的;按分值排序,若分值相同就按成员对象大小排序。
  
    
    
  
typedef struct zskiplist { //头节点和尾节点 struct zskiplistNode *header, *tail;
//节点数量 unsigned long length;
//表中节点最大层数 int level; } zskiplist;
  
    
    
  
typedef struct zskiplistNode { //层 struct zskiplistLevel { //前进指针,用于表头向表尾方向访问节点 struct zskiplistNode *forward; //跨度,记录两个节点之间的关系 unsigned int span; } level [];
//后退指针,用于表尾向表头方向访问节点,每次只能后退一个节点 struct zskiplistNode *backward; //分值,表中的节点按分值大小排序 double score; //成员对象,指向一个SDS对象 robj *obj; } zskiplistNode;
跳跃表是什么
一般地单链表,查找某个数据的时间复杂度是o(n), 如何提高效率?
如图所示,可以建立索引层的方法。将每2个节点抽取一个节点到上一层,被抽取出来的称之为【索引层】。
此时相当于HashMap存储了一个key值,其value是对应原始连接中的2个节点组成的List。
Redis跳跃表

第二级索引:把第一级索引的2个节点再抽取出来组成二级索引,此时查找遍历的数据更少了。
Redis跳跃表

如此反复,当节点数量非常多的时候,这种添加索引的方式会使得查询效率提高地非常明显。
这样,由索引和原始链表构成的一种数据结构就是跳跃表。

跳跃表的性能
(1)跳跃表的高度logn
有n个节点的链表,如果每2个链表组成一个索引,那么,
  • 第1级索引个数为n/2
  • 第2级索引个数为n/4
  • ....
  • 第h级索引个数为n/2^h
假设最后一级索引的个数为2,则h+1 = logn,算上最底层的一层链表,跳表的高度H=logn.

(2)跳跃表的时间复杂 度o(logn)
每一级只需要遍历2个节点即可,因此时间复杂度是 o(logn)
为什么是2个?因为我们是每两个结点提取一个结点建立索引,最高一级索引只有两个结点,然后下一层索引比上一层索引两个结点之间增加了一个结点,也就是上一层索引两结点的中值,看到这里是不是想起来我们前边讲过的二分查找,每次我们只需要判断要找的值在不在当前结点和下一个结点之间即可。

(3)空间复杂度o(n)
将每一层索引的节点数加起来,这是一个等比数列:
n/2 + n/4 + .... + 8 + 4 + 2 = n-2。
所以跳表的空间复杂度 o(n)
如果想要减少索引所占的内存空间,可以考虑每3个或者5个抽取一个索引。

跳跃表的插入和删除效率
(1)插入 O(logn)
跳表的查询的时间复杂度为 O(logn),因为找到位置之后插入和删除的时间复杂度很低,为 O(1),所以最终插入和删除的时间复杂度也为 O(longn)。
如果我们不停的向跳表中插入元素,就可能会造成两个索引点之间的结点过多的情况。结点过多的话,我们建立索引的优势也就没有了。所以我们需要维护索引与原始链表的大小平衡,也就是结点增多了,索引也相应增加,避免出现两个索引之间结点过多的情况,查找效率降低。
跳表是通过一个随机函数来维护这个平衡的, 当我们向跳表中插入数据的的时候,我们可以选择同时把这个数据插入到索引里,那我们插入到哪一级的索引呢,这就需要随机函数,来决定我们插入到哪一级的索引中。

(2)删除
删除操作的话,如果这个结点在索引中也有出现,我们除了要删除原始链表中的结点,还要删除索引中的。因为单链表中的删除操作需要拿到要删除结点的前驱结点,然后通过指针操作完成删除。所以在查找要删除的结点的时候,一定要获取前驱结点。当然,如果我们用的是双向链表,就不需要考虑这个问题了。

总结
跳跃表的特点:
  • 查找、插入、删除效率较高
  • 可以实现范围查找
  • 迭代输出有序数据
  • 跳表和红黑树的查找、插入、删除性能不相上下
  • 红黑树无法支持区间查找






以上是关于Redis跳跃表的主要内容,如果未能解决你的问题,请参考以下文章

Redis跳跃表

Redis源码解读——跳跃表

Redis源码解读——跳跃表

Redis中的跳跃表

redis源码跳跃表(zskiplist)

Redis底层解析跳跃表