深入剖析:Redis分布式锁事故解析与优化实践
创始人
2025-01-09 19:45:09
0 次浏览
0 评论
分布式锁的三种实现方式面试
它有保证。缺点是实现相对复杂,性能可能比基于Redis的分布式锁稍低。
不过,在安全性要求极高的情况下,基于Zookeeper的分布式锁仍然是一个绝佳的选择。
综上所述,分布式锁的三种实现方式各有优缺点,应根据具体情况和需求进行选择。
在实际应用中,需要综合考虑性能、可靠性、易用性等因素,才能有效解决分布式锁的相关问题,保证系统的稳定运行。
一次由Redis分布式锁造成的重大事故,避免以后踩坑!
使用基于Redis的分布式锁如今已经不是什么新鲜事了。本文主要是根据我们实际项目中重分布锁导致的事故的分析和解决。
我们项目中的紧急订单是使用分布式锁解决的。
经营者曾举办飞天茅台抢购活动,库存100瓶,但已售空!要知道,这个地球上就缺飞天茅台!!!这次事故定性为P0级重大事故……我们可以放心。
整个项目组的业绩都被扣了~~事故发生后,CTO点名我,让我带头处理之前购买活动的接口,但是这次为什么超卖了?原因是:之前的抢购产品并不紧缺,而这次的抢购对象却是飞天茅台。
通过数据分析,所有数据初步增长。
活动现场的热烈程度可想而知。
话不多说,直接上核心代码,机密部分已经用伪代码处理了。
。
。
上面的代码通过10秒的分布式锁过期时间来保证业务逻辑有足够的执行时间。
try-finally语句块用于保证锁的及时释放。
库存也在业务代码中进行验证。
看起来很安全~别担心,继续分析。
。
。
飞天茅台的抢购活动吸引了大量新用户下载注册我们的APP。
其中,有不少羊毛地段采用专业方式注册新用户收羊毛、刷单。
当然,我们的用户系统提前做好了防范,配备了阿里云人机验证、三因素认证和自研风控系统,拦截了大量非法用户。
在这里忍不住竖起大拇指~但也正因为如此,用户服务始终处于较高的运行负载下。
抢购活动一开始,大量的用户验证请求就冲击了用户服务。
这导致用户服务网关的响应延迟很短。
部分请求响应时间超过10秒。
但由于HTTP请求超时,我们设置为30s,导致用户验证时接口阻塞。
10s后,分配的Lock已经过期,如果这时候有新的请求进来,就可以获取到锁,也就是说锁被覆盖了。
这些阻塞的接口执行完后,会再次执行释放锁的逻辑,将锁释放给其他线程,导致新的请求竞争锁。
这确实是一个极其糟糕的循环。
此时我们只能依靠库存验证,但是库存验证不是非原子的,它使用getandcompare方法。
高当代场景中,存在严重的安全风险,主要集中在三个地方:通过上述分析,问题的主要原因是库存验证严重依赖分布式锁。
因为分布式锁正常设置和删除的时候,库存确认是没有问题的。
然而,当分布式锁不安全可靠时,仓库验证就没用了。
了解了原因之后,我们就可以对症下药了。
分布式锁的一个相对安全的定义,实现相对安全:set和share是一一映射的,其他完成的锁不会被删除。
从实际角度来看,即使set和part能够一一映射,也无法保证业务的绝对安全。
因为锁的过期时间总是有限的,除非不设置过期时间或者过期时间设置得很长,这个还会引起其他问题。
因此它是没有意义的。
要实现相对安全的分布式锁,必须信任密钥的值。
当你释放锁的时候,通过value的值来保证不被删除。
我们基于LUA脚本实现原子get和compare,如下:我们使用LUA脚本实现安全解锁。
实现安全库存验证如果我们对并发有更深入的了解,我们会发现像getandcompare/readandsave这样的操作都是非原子的。
如果我们想要实现原子性,我们也可以使用LUA脚本来实现。
但在我们的例子中,由于抢购活动期间每个订单只能下一瓶,所以可以不基于LUA脚本来实现,而是基于redis本身的原子性来实现。
原因是:发现代码中没有Stock确认完全是多余的。
经过上述增强代码的分析,我们决定创建一个新的DistributedLocker类专门用于处理分布式锁。
仔细思考分布式锁是否有必要或者改进之后,我们其实可以发现,依靠redis本身的原子性来减库存,就能保证不会超卖。
是的,但是如果没有这一层锁,所有传入的请求都会经过业务逻辑,因为它们依赖于其他系统,这会导致其他系统的压力增大。
这会增加性能损失和服务不稳定,得不偿失。
基于分布式锁可以在一定程度上拦截一些流量。
分布式锁的选择有人建议使用RedLock来实现分布式锁。
RedLock具有更高的可靠性,但代价是牺牲了一些性能。
在这种情况下,这种可靠性的提升远不如性能的提升划算。
对于可靠性要求极高的场景,可以使用RedLock。
我们再次思考是否需要分布式锁,还是因为bug需要修复并上线,我们对其进行了优化,并在测试环境中进行了压力测试,并立即上线实施。
确实,这种优化已经被证明是成功的,性能略有提升,而且分布式锁失效时也没有出现超卖的情况。
但还有优化的空间吗?一些!由于服务是分布在集群中的,我们可以将库存均匀的分配到集群中的每台服务器上,并通过广播的方式通知集群中的每台服务器。
网关层使用基于用户ID的哈希算法来确定请求哪个服务器。
这样就可以根据申请缓冲区实现库存扣减和考核。
性能进一步提升!通过上面的改造,我们就完全不再需要依赖redis了。
无论是性能还是安全性都可以进一步提升!当然,这个方案并没有考虑到机器动态伸缩等复杂场景。
如果还需要考虑这些,最好还是考虑一下分布式锁的方案。
总结:稀缺商品的超卖绝对是一大不幸。
如果超售量较大,甚至会对平台造成非常严重的业务影响和社会影响。
经过这次事故,我意识到项目中的任何一行代码都不能掉以轻心,否则在某些场景下这些正常工作的代码会成为致命的杀手!对于开发商来说,在制定开发计划时必须仔细评估计划。
我们如何全面评估该计划?继续学习!
一对一源码,基于Redis实现分布式锁的方式
Redis方案一:基于setnx方案实现分布式锁的一对一源码(不推荐)。该方案存在的问题:1.可能出现锁定2.锁在保持期内过期方案二:setExtension命令针对方案一中存在的问题。
在redis版本>=2.8中;设置命令已被扩展。
解决这个setnx+expire原子性问题。
命令如下:将两条setnx+expire命令合并为一条,保证原子性。
代码示例:大多数业务场景都可以通过方案2来解决。
如果某些业务条件需要锁定;可以参考重新入境。
方案3:通过Lua脚本设置setnx+expire命令包可以打包多个命令通过Lua脚本执行示例代码:大多数业务情况可以通过方案2和方案3解决。
如果某些业务条件需要锁定;可以参考重新入境。
方案4是redisson(推荐)redisson是基于redisjavaclient的,底层实现是分布式密钥;执行了很多读写锁等底层实现。
请参阅官方网站了解更多详情。
核心代码:Redisson对比上述方案;实际上,除了已经做了很多封装之外。
实现类似;它使用起来非常简单,并且内置看门狗更新机制。
测试代码:测试结果如下:由于看门狗超时设置为10秒;每3秒进行一次更新(1/3*10),所以从6970ttl到8834的变化主要是由于更新。
Redis分布式锁有什么缺陷?
Redis分布式锁无法解决超时问题。分布式块有超时期限如果程序执行超过了块的超时期限,就会出现问题。
相关文章
深入解析Java:面向对象编程特性与实现...
2025-01-12 05:04:37C语言未知长度数组定义及实例解析
2024-12-15 04:24:26Docker部署Minio容器教程:详细...
2024-12-25 01:21:15C语言实现选择排序:数字输入排序问题解答
2024-12-17 09:21:19SQL INSERT语句详解:高效插入数...
2024-12-23 16:14:53Java使用Jedis操作Redis入门...
2024-12-27 02:17:32Docker文件复制指南:从容器到主机的...
2024-12-15 06:44:57Docker容器访问问题解析:解决doc...
2024-12-16 23:40:11Java实现:输入10个数排序及统计正负...
2024-12-27 03:19:48Redis缓存一致性策略解析:数据库与缓...
2024-12-31 12:27:21最新文章
12
2025-01
12
2025-01
12
2025-01
12
2025-01
12
2025-01
12
2025-01
12
2025-01
12
2025-01
12
2025-01
12
2025-01
热门文章
1
Redisson分布式锁深度解析:Red...
Redis实现分布式锁+Redisson源码解析在某些场景下,多个进程需要以互斥...
2
深度解析Docker:容器技术提升应用部...
docker是什么Docker是一种强大的开源容器技术,它将应用程序及其所有依赖...
3
C语言实现:如何判断一个整数是否为质数?
C语言输入一个整数,判断是否是质数?#include//头文件intmain()...
4
K8s弃用Docker背后的故事及Doc...
K8s为什么要弃用Docker?在讨论K8s抛弃Docker的话题时,我们首先需...
5
C语言文本输入输出教程:安全高效处理字符...
C语言怎么变成文字?如果你想用C语言输入输出文本,其实很简单。您必须首先定义一个...
6
深入解析Java:面向对象编程特性与实现...
Java语言的特点,实现机制和体系结构。中的任何实体都可以被视为一个对象。对象通...
7
三款免费Docker管理工具,提升您的可...
3款免费又好用的Docker可视化管理工具在Docker的世界里,命令行工具无疑...
8
C语言期末编程题解析:完整程序代码分享
C语言期末考试编程代码函数题?按照题目要求编写的完整程序如下(见图,图中重复的部...
9
轻松掌握:Redis键值查看技巧,两种方...
如何读取redis中的key值中的结果我们希望它能帮助您使用它。怎么查看redi...
10
SQL DELETE语句:详解及不同删除...
请问Sql的DELETE语句怎么写delete表示删除表中的数据示例:delet...