跳跃表原理及redis跳跃表的应用

跳跃表的实现还是一个链表,是一个有序的链表,在遍历的时候基于比较,但普通链表只能遍历,跳跃表加入了一个层(也叫索引)的概念,层数越高的元素越少,每次先从高层查找,再逐渐降层,直到找到合适的位置。从图中可以看到高层的节点远远少于底层的节点数,从而实现了跳跃式查找。

redis只在两个地方用到了跳跃表,一个是有序集合键(zset),另一个是在集群节点用作内部数据结构。

其实跳跃表是受多层链表的想法启发设计得来的。而上层指针链表都是在下层的基础上选取的。

如果上一层的链表的节点个数,是下面一层的节点个数的一半,这样查找就非常类似于一个二分查找

跳跃表用随机的方式(抛硬币)来解决层数的问题,好奇为什么不直接用二分查找的方式去解决问题, 查找了知乎的回答,找到了答案:

试想一下,如果我们结构上强制着二分查找,相邻的两层链表上的节点个数严格按照2:1的对应关系,那么在插入新节点的时候,就会打乱这层对应关系,要维护这层关系,又必须把新插入的节点后边的所有节点重新进行调整,这又让时间复杂度退化为O(N),删除数据也有同样的问题。

跳跃表为了避免这一问题,就采用了随机层数的方式来巧妙的解决。

不要求上下相邻两层链表之间的节点个数有严格的对应关系,而是为每个节点随机出一个层数(level),新插入的节点就会根据自己的层数决定该节点是否在这层的链表上

本文通过操作redis zset来实际感受

redis命令(可以看 关于Redis的Zset使用方法_jesus小超的博客-CSDN博客_redis zset

在公司的laravel中,直接使用 Illuminate\Support\Facades\Redis 

可以让我在 Redis facade 调用任何的 Redis 命令。Laravel 使用魔术方法来传递命令至 Redis 服务器,所以可以很方便使用zset

        Redis::zadd('runoobkey', 2, 'redis');
        Redis::zadd('runoobkey', 3, 'redis');
        Redis::zadd('runoobkey', 4, 'redis');
        dd(Redis::zrange( 'runoobkey', 0, 10, 'WITHSCORES'));

但是这里发现一个需要注意的问题,就是如果存入的score是浮点数,那么有可能redis那边取出时会出现精度问题,这是我这边的案例:

$arr = [
    [sprintf("%.1f", 3.9), 'redis3.9'], 
    [sprintf("%.1f", 3.6), 'redis3.6'],
    [sprintf("%.1f", 3.2), 'redis3.2'], 
    [sprintf("%.1f", 3.8), 'redis3.8'],
    [sprintf("%.1f", 3.5), 'redis3.5'], 
    [4, 'redis4']
];
Redis::del('runoobkey');
foreach ($arr as $v) {
    Redis::zadd('runoobkey', $v[0], $v[1]);
}
dd($arr, Redis::zrange( 'runoobkey', 0, 10, 'WITHSCORES'));

sprintf("%.1f",xxx)保证了在laravel端时数据传入就是一位小数,打印结果为

array:6 [▼
  0 => array:2 [▼
    0 => "3.9"
    1 => "redis3.9"
  ]
  1 => array:2 [▼
    0 => "3.6"
    1 => "redis3.6"
  ]
  2 => array:2 [▼
    0 => "3.2"
    1 => "redis3.2"
  ]
  3 => array:2 [▼
    0 => "3.8"
    1 => "redis3.8"
  ]
  4 => array:2 [▼
    0 => "3.5"
    1 => "redis3.5"
  ]
  5 => array:2 [▼
    0 => 4
    1 => "redis4"
  ]
]
array:6 [▼
  "redis3.2" => "3.2000000000000002"
  "redis3.5" => "3.5"
  "redis3.6" => "3.6000000000000001"
  "redis3.8" => "3.7999999999999998"
  "redis3.9" => "3.8999999999999999"
  "redis4" => "4"
]

因此在取出时要四舍五入处理下

而在公司之前的spring boot项目中,因为使用的是org.springblade.core.tool.utils.RedisUtil

该类方法只支持SET(字符串) 、 HMSET (哈希)两种redis格式,因此不好做修改,只能在下个项目考虑更换调用的redis类来使用有序集合,从而在代码中使用redis的跳跃链表优化性能。

目前网上见到最多的案例是使用redis.clients.jedis,同事的建议是jedis已经很落后了,不建议使用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值