循序渐进,阿里架构师看完都赞叹的Redis分布式锁原理分析

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

其次本次 客户端操作完成后, 需要让 其它客户端继续执行

1、客户端一存放一个标志位, 如果添加成功, 操作减优惠券数量操作

2、客户端二添加标志位失败, 本次减库存操作失败(或继续尝试获取等)

3、客户端一优惠券操作完成后, 需要将标志位释放, 以便其余客户端对库存进行操作

第一版 setnx

向 Redis 中添加一个 lockKey 锁标志位, 如果添加成功则能够继续向下执行扣减优惠券数量操作, 最后再释放此标志位

由于使用的是 Spring 提供的 Redis 封装的 Start 包, 所有有些命令与 Redis 原生命令不相符

setIfAbsent(key, val) -> setnx(key, val)

加了简单的几行代码, 一个简单的分布式锁的雏形就出来了

第二版 expire

上面第一版基于 setnx 命令实现分布式锁的缺陷也是很明显的, 那就是 一定情况下可能发生死锁

画个图, 举个例子说明哈

上图说明, 线程1在成功获取锁后, 执行流程时异常结束, 没有执行释放锁操作, 这样就会 产生死锁

如果方法执行异常导致的线程被回收, 那么可以将解锁操作放到 finally 块中

但是还有存在死锁问题, 如果获得锁的线程在执行中, 服务被强制停止或服务器宕机, 锁依然不会得到释放

这种极端情况下我们还是要考虑的, 毕竟不能只想着服务没问题对吧

对 Redis 的 锁标志位加上过期时间 就能很好的防止死锁问题, 继续更改下程序代码

虽然 小红旗处 对分布式锁添加了过期时间, 但依然无法避免极端情况下的死锁问题

那就是如果在客户端加锁成功后, 还没有设置过期时间时宕机

如果想要避免添加锁时死锁, 那就对添加锁标志位 & 添加过期时间命令 保证一个原子性, 要么一起成功, 要么一起失败

第三版 set

我们的添加锁原子命令就要登场了, 从 Redis 2.6.12 版本起, 提供了可选的 字符串 set 复合命令

SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

可选参数如下:

  • EX: 设置超时时间,单位是秒
  • PX: 设置超时时间,单位是毫秒
  • NX: IF NOT EXIST 的缩写,只有 KEY不存在的前提下 才会设置值
  • XX: IF EXIST 的缩写,只有在 KEY存在的前提下 才会设置值

继续完善分布式锁的应用程序, 代码如下:

我使用的 2.0.9.RELEASE 版本的 SpringBoot, RedisTemplate 中不支持 set 复合命令, 所以临时换个 Jedis 来实现

加锁以及设置过期时间确实保证了原子性, 但是这样的分布式锁就没有问题了么?

我们根据图片以及流程描述设想一下这个场景

1、线程一获取锁成功, 设置过期时间五秒, 接着执行业务逻辑

2、接着线程一获取锁后执行业务流程, 执行的时间超过了过期时间, 锁标志位过期进行释放, 此时线程二获取锁成功

3、然鹅此时线程一执行完业务后, 开始执行释放锁的流程, 然后顺手就把线程二获取的锁释放了

如果线上真的发生上述问题, 那真的是xxx, 更甚者可能存在线程一将线程二的锁释放掉之后, 线程三获取到锁, 然后线程二执行完将线程三的锁释放

第四版 verify value

事当如今, 只能创建辨别客户端身份的唯一值了, 将加锁及解锁归一化, 上代码~

image

这一版的代码相当于我们添加锁标志位时, 同时为每个客户端设置了 uuid 作为锁标志位的 val, 解锁时需要判断锁的 val 是否和自己客户端的相同, 辨别成功才会释放锁

但是上述代码执行业务逻辑如果抛出异常, 锁只能等待过期时间, 我们可以将解锁操作放到 finally 块

大眼一看, 上上下下实现了四版分布式锁, 也该没问题了吧

真相就是: 解锁时, 由于判断锁和删除标志位并不是原子性的, 所以可能还是会存在误删

1、线程一获取锁后, 执行流程balabala… 判断锁也是自家的, 这时 CPU 转头去做别的事情了, 恰巧线程一的锁过期时间到了

2、线程二此时顺理成章的获取到了分布式锁, 执行业务逻辑balabala…

3、线程一再次分配到时间片继续执行删除操作

解决这种非原子操作的方式只能 将判断元素值和删除标志位当作一个原子操作

第五版 lua

很不友好的是, del 删除操作并没有提供原子命令, 所以我们需要想点办法

Redis在 2.6 推出了脚本功能, 允许开发者使用 Lua 语言编写脚本传到 Redis 中执行

使用 Lua 脚本有什么好处呢?

1、减少网络开销

原本我们需要向 Redis 服务请求多次命令, 可以将命令写在 Lua 脚本中, 这样执行只会发起一次网络请求

2、原子操作

Redis 会将 Lua 脚本中的命令当作一个整体执行, 中间不会插入其它命令

3、复用(大家自己探索哈)

客户端发送的脚步会存储 Redis 中, 其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑

那我们编写一个简单的 Lua 脚本实现原子删除操作

重点就在 Lua 脚本这一块, 重点说一下这块的逻辑

script 脚本就是我们在 Redis 中执行的 Lua 脚本, 后面跟的两个 List 分别是 KEYS、ARGV

cache.eval(script, Lists.newArrayList(lockKey), Lists.newArrayList(lockValue));

KEYS[1]: lockKey

ARGV[1]: lockValue

代码不是很多, 也比较简单, 就是在 Java 中代码实现的逻辑放到了一个 Lua 脚本中

获取 KEYS[1] 对应的 Val

local cliVal = redis.call(‘get’, KEYS[1])

判断 KEYS[1] 与 ARGV[1] 是否保持一致

if(cliVal == ARGV[1]) then

删除 KEYS[1]

redis.call(‘del’, KEYS[1])
return ‘OK’
else
return nil
end

最后我们该如何学习?

1、看视频进行系统学习

这几年的Crud经历,让我明白自己真的算是菜鸡中的战斗机,也正因为Crud,导致自己技术比较零散,也不够深入不够系统,所以重新进行学习是很有必要的。我差的是系统知识,差的结构框架和思路,所以通过视频来学习,效果更好,也更全面。关于视频学习,个人可以推荐去B站进行学习,B站上有很多学习视频,唯一的缺点就是免费的容易过时。

另外,我自己也珍藏了好几套视频资料躺在网盘里,有需要的我也可以分享给你:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2、读源码,看实战笔记,学习大神思路

“编程语言是程序员的表达的方式,而架构是程序员对世界的认知”。所以,程序员要想快速认知并学习架构,读源码是必不可少的。阅读源码,是解决问题 + 理解事物,更重要的:看到源码背后的想法;程序员说:读万行源码,行万种实践。

Spring源码深度解析:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Mybatis 3源码深度解析:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Redis学习笔记:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Spring Boot核心技术-笔记:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3、面试前夕,刷题冲刺

面试的前一周时间内,就可以开始刷题冲刺了。请记住,刷题的时候,技术的优先,算法的看些基本的,比如排序等即可,而智力题,除非是校招,否则一般不怎么会问。

关于面试刷题,我个人也准备了一套系统的面试题,帮助你举一反三:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

只有技术过硬,在哪儿都不愁就业,“万般带不去,唯有业随身”学习本来就不是在课堂那几年说了算,而是在人生的旅途中不间断的事情。

人生短暂,别稀里糊涂的活一辈子,不要将就。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-hwjQJjgY-1713704968035)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值