【Redis】底层跳表实现

本文探讨了Redis中ZSet数据类型的实现,特别是使用压缩列表和跳表的情况。当元素数量或大小超过限制时,跳表通过多级索引提高查找性能,平均时间复杂度为O(logN),对比红黑树和二叉树,跳表在范围查找和实现简易性上有优势。
摘要由CSDN通过智能技术生成

先巩固Redis的数据类型以及底层的数据结构:

ZSet(有序集合)可以使用两种不同的内部数据结构来表示:压缩列表(ziplist)和跳跃表(skiplist)。

跳表是redis底层SortedSet(ZSet)的数据结构实现,是ZSet的灵魂所在;set是一个无序集合,而ZSet是有序集合。

ZSet使用压缩列表情况:

1.有序集合保存的元素数量小于128个;

2.有序集合保存的所有元素的长度小于64个字节

如果元素数量或元素大小超过了以上限制就会转换为跳表存储。

 跳表

为提升查找的性能,Redis就引⼊了跳表,跳表在链表的基础上,给链表增加了多级的索引,通过

索引可以⼀次实现多个节点的跳跃,提⾼性能。

链表

 

Redis中的跳表

 

Redis中跳表单个节点定义:

 

ele:SDS结构,⽤来存储数据。

score:节点的分数,浮点型数据

backward:指向上⼀个节点的回退指针,⽀持从表尾向表头遍历,也就是ZREVRANGE这个命令

level:level结构体数组就是⽤来保存各个索引层级的forward指针和span信息的。具体来说,level[0] 表示最底层索引,level[1]为第⼆层索引,以此类推。 higher level的forward指向更远的节点,可以快速 遍历。span记录索引跨度。通过维护多级索引,跳表可以通过较⾼层级的索引快速定位到⽬标节点附 近,然后再在底层索引线性查找,平均时间复杂度为O(logN)。所以,level数组在跳表中的作⽤就是记录 多级索引的数据,让跳表可以实现更快的查找、遍历、排序等操作。

 举个例子:

例如查找元素abcd:

 

如果要查找「元素:abcd,权重:4」的节点,查找的过程:

  1. 1.先从头节点的最⾼层开始,L2 指向了「元素:abc,权重:3」节点,这个节点的权重⽐要查找节点的⼩,所以要访问该层上的下⼀个节点;
  2. 但是该层的下⼀个节点是空节点( leve[2]指向的是空节点),于是就会跳到「元素:abc,权重:3」节点的下⼀层去找,也就是 leve[1];
  3. 「元素:abc,权重:3」节点的 leve[1] 的下⼀个指针指向了「元素:abcde,权重:4」的 节点,然后将其和要查找的节点⽐较。虽然「元素:abcde,权重:4」的节点的权重和要查 找的权重相同,但是当前节点的 SDS 类型数据「⼤于」要查找的数据,所以会继续跳到「元 素:abc,权重:3」节点的下⼀层去找,也就是 leve[0];
  4. 「元素:abc,权重:3」节点的 leve[0] 的下⼀个指针指向了「元素:abcd,权重:4」的节 点,该节点正是要查找的节点,查询结束。

redis跳表的单个节点有⼏层

层次的决定,需要⽐较随机,才能在各个场景表现出较为平均的性能,这⾥Redis使⽤概率均衡的思 路来确定新插⼊节点的层数:Redis跳表决定每⼀个节点,是否能增加⼀层的概率为25%,⽽最⼤层数限 制在Redis5.0是64层在Redis7.0是32层。

redis跳表的性能优化了多少

平均时间复杂度都是O(logn),区别是⼆叉树最坏情况下也是O(logn)⽐较稳定,⽽跳表的最坏时间复杂度是O(N)。当然,实际的⽣产过程 中,体现出来的基本都是跳表的平均时间复杂度。 前⾯也提到,有序集合⽆论是查找、还是增加删除元素,都是需要先定位到数据位置,所以跳表将这三个操作的时间复杂度,都从O(N)降低到了O(logn)。

为什么使用跳表不适用红黑树或二叉树呢

1.因为跳表可以快速的范围查找;

2.跳表的实现比红黑树 简单易懂。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小俱的一步步

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值