问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

一次由Redis分布式锁造成的重大事故,避免以后踩坑!

发布网友 发布时间:2024-10-03 22:21

我来回答

1个回答

热心网友 时间:2024-11-19 05:31

基于Redis使用分布式锁在当今已经不是什么新鲜事了。本篇文章主要是基于我们实际项目中因为redis分布式锁造成的事故分析及解决方案。

我们项目中的抢购订单采用的是分布式锁来解决的。有一次,运营做了一个飞天茅台的抢购活动,库存100瓶,但是却超卖了!要知道,这个地球上飞天茅台的稀缺性啊!!!事故定为P0级重大事故...只能坦然接受。整个项目组被扣绩效了~~事故发生后,CTO指名点姓让我带头冲锋来处理,好吧,冲~

经过一番了解后,得知这个抢购活动接口以前从来没有出现过这种情况,但是这次为什么会超卖呢?原因在于:之前的抢购商品都不是什么稀缺性商品,而这次活动居然是飞天茅台,通过埋点数据分析,各项数据基本都是成倍增长,活动热烈程度可想而知!话不多说,直接上核心代码,机密部分做了伪代码处理。。。

以上代码,通过分布式锁过期时间有效期10s来保障业务逻辑有足够的执行时间;采用try-finally语句块保证锁一定会及时释放。业务代码内部也对库存进行了校验。看起来很安全啊~ 别急,继续分析。。。

飞天茅台抢购活动吸引了大量新用户下载注册我们的APP,其中,不乏很多羊毛党,采用专业的手段来注册新用户来薅羊毛和刷单。当然我们的用户系统提前做好了防备,接入阿里云人机验证、三要素认证以及自研的风控系统等各种十八般武艺,挡住了大量的非法用户。此处不禁点个赞~

但也正因如此,让用户服务一直处于较高的运行负载中。

抢购活动开始的一瞬间,大量的用户校验请求打到了用户服务。导致用户服务网关出现了短暂的响应延迟,有些请求的响应时长超过了10s,但由于HTTP请求的响应超时我们设置的是30s,这就导致接口一直阻塞在用户校验那里,10s后,分布式锁已经失效了,此时有新的请求进来是可以拿到锁的,也就是说锁被覆盖了。这些阻塞的接口执行完之后,又会执行释放锁的逻辑,这就把其他线程的锁释放了,导致新的请求也可以竞争到锁~这真是一个极其恶劣的循环。

这个时候只能依赖库存校验,但是偏偏库存校验不是非原子性的,采用的是get and compare的方式,超卖的悲剧就这样发生了~~~

仔细分析下来,可以发现,这个抢购接口在高并发场景下,是有严重的安全隐患的,主要集中在三个地方:

通过以上分析,问题的根本原因在于库存校验严重依赖了分布式锁。因为在分布式锁正常set、del的情况下,库存校验是没有问题的。但是,当分布式锁不安全可靠的时候,库存校验就没有用了。

知道了原因之后,我们就可以对症下药了。

实现相对安全的分布式锁

相对安全的定义:set、del是一一映射的,不会出现把其他现成的锁del的情况。从实际情况的角度来看,即使能做到set、del一一映射,也无法保障业务的绝对安全。因为锁的过期时间始终是有界的,除非不设置过期时间或者把过期时间设置的很长,但这样做也会带来其他问题。故没有意义。

要想实现相对安全的分布式锁,必须依赖key的value值。在释放锁的时候,通过value值的唯一性来保证不会勿删。我们基于LUA脚本实现原子性的get and compare,如下:

我们通过LUA脚本来实现安全地解锁。

实现安全的库存校验

如果我们对于并发有比较深入的了解的话,会发现想 get and compare/ read and save 等操作,都是非原子性的。如果要实现原子性,我们也可以借助LUA脚本来实现。但就我们这个例子中,由于抢购活动一单只能下1瓶,因此可以不用基于LUA脚本实现而是基于redis本身的原子性。原因在于:

发现没有,代码中的库存校验完全是“画蛇添足”。

改进之后的代码

经过以上的分析之后,我们决定新建一个DistributedLocker类专门用于处理分布式锁。

深度思考

分布式锁有必要么

改进之后,其实可以发现,我们借助于redis本身的原子性扣减库存,也是可以保证不会超卖的。对的。但是如果没有这一层锁的话,那么所有请求进来都会走一遍业务逻辑,由于依赖了其他系统,此时就会造成对其他系统的压力增大。这会增加的性能损耗和服务不稳定性,得不偿失。基于分布式锁可以在一定程度上拦截一些流量。

分布式锁的选型

有人提出用RedLock来实现分布式锁。RedLock的可靠性更高,但其代价是牺牲一定的性能。在本场景,这点可靠性的提升远不如性能的提升带来的性价比高。如果对于可靠性极高要求的场景,则可以采用RedLock来实现。

再次思考分布式锁有必要么

由于bug需要紧急修复上线,因此我们将其优化并在测试环境进行了压测之后,就立马热部署上线了。实际证明,这个优化是成功的,性能方面略微提升了一些,并在分布式锁失效的情况下,没有出现超卖的情况。

然而,还有没有优化空间呢?有的!

由于服务是集群部署,我们可以将库存均摊到集群中的每个服务器上,通过广播通知到集群的各个服务器。网关层基于用户ID做hash算法来决定请求到哪一台服务器。这样就可以基于应用缓存来实现库存的扣减和判断。性能又进一步提升了!

通过以上的改造,我们就完全不需要依赖redis了。性能和安全性两方面都能进一步得到提升! 当然,此方案没有考虑到机器的动态扩容、缩容等复杂场景,如果还要考虑这些话,则不如直接考虑分布式锁的解决方案。

总结

稀缺商品超卖绝对是重大事故。如果超卖数量多的话,甚至会给平台带来非常严重的经营影响和社会影响。经过本次事故,让我意识到对于项目中的任何一行代码都不能掉以轻心,否则在某些场景下,这些正常工作的代码就会变成致命杀手!对于一个开发者而言,则设计开发方案时,一定要将方案考虑周全。怎样才能将方案考虑周全?唯有持续不断地学习!
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
李卓彬工作简历 林少明工作简历 广东工业职业技术学院怎么样 郑德涛任职简历 唐新桂个人简历 土地入股的定义 ups快递客服电话24小时 贷款记录在征信保留几年? 安徽徽商城有限公司公司简介 安徽省徽商集团新能源股份有限公司基本情况 Redis高并发缓存架构性能优化实战 Redisson解决Redis分布式锁提前释放问题 一个Redis分布式锁的实现引发的思考 普桑水箱散热风扇优缺点是什么 Redis三种常用的缓存读写策略 ...开空调时只有低速风扇会转。高速不转是怎么回事,水温不高,空调散热... 好''字的笔画顺序查询 急需一个带芳字的网名 要求 可爱简单点的 但是一定要独特哟 谢谢!_百度... 给我想一个QQ网名,网名里面必须要带有一个芳字,要带有爱情的,拜托了... 好字的笔顺笔画顺序如下? 电影 求一个能下载 投名状、集结号的网址 100分求:《投名状》恶搞版《投资状》下载地址! 厨房怎么“装”风水才够吉利? 农村自建房厨房风水之五大门口风水禁忌,看你家是否已犯? 形容体态好的成语 秋梨膏不适合什么人吃 秋梨膏对哪些人不好 怎么样才可以减肥?吃苹果醋可以吗?早上吃牛奶加蜂蜜,中午吃饭,晚上吃水 ... 卧室没有地漏为什么都会出现地沟蝇?今年才发现有的 地沟堵了找地漏位置 ...在此说破。祝愿家人和自己平平安安健健康康,大吉大利,财源滚滚来... 为什么小米手机总是自动接听电话 读书梦见和不认识的人抬不认识的尸体,好多尸水,然后一不小心全部掉我... 梦见一个女人死在水里,尸体在水里随着水的方向在走,我让别人把她救上 ... ...女儿找不到了,在水里发现了她的衣服,没有看到尸体,我以为被淹死了... 茶叶包装上的保质期是什么样的“用意”? 关于"抵制不良诱惑,做自己的主人。〃的一份倡议书 高昌区青少年暑期文明上网倡议书 鲁迅故居要买票吗_北京鲁迅故居参观老年人用预约吗 早晨起来空腹可以吃柚子吗 去绍兴鲁迅故居等附近景点要多少 早上吃了两个包子一杯豆浆三片柚子会胖吗 晚吃早点是不是对身体不好容易衰老 得到标况下448ml 的氢气,求需要多少克 化学计算题,快 产品分析丨音遇 一场年轻人的音乐狂欢 1899能买什么手机 上几年我出了车祸,我这本不富裕的家,我爸妈为了救我还欠下一大笔钱... 20岁,她的钱都被父母花光了,遇到这样的父母,就算了吧 曾经我爱的人说我缺少一个男人的阳刚之气和气场所以她家人不同意把我... 松茸有哪些好处