安庆大理运城常德铜陵江西
投稿投诉
江西南阳
嘉兴昆明
铜陵滨州
广东西昌
常德梅州
兰州阳江
运城金华
广西萍乡
大理重庆
诸暨泉州
安庆南充
武汉辽宁

Redis分布式锁使用不当,酿成一个重大事故,超卖了100瓶

11月23日 程染筱投稿
  来源:juejin。cnpost6854573212831842311
  基于Redis使用分布式锁在当今已经不是什么新鲜事了。
  本篇文章主要是基于我们实际项目中因为redis分布式锁造成的事故分析及解决方案。我们项目中的抢购订单采用的是分布式锁来解决的,有一次,运营做了一个飞天茅台的抢购活动,库存100瓶,但是却超卖了100瓶!要知道,这个地球上飞天茅台的稀缺性啊!!!
  事故定为P0级重大事故。。。只能坦然接受。整个项目组被扣绩效了
  事故发生后,CTO指名点姓让我带头冲锋来处理。好吧,冲事故现场
  经过一番了解后,得知这个抢购活动接口以前从来没有出现过这种情况,但是这次为什么会超卖呢?
  原因在于:之前的抢购商品都不是什么稀缺性商品,而这次活动居然是飞天茅台,通过埋点数据分析,各项数据基本都是成倍增长,活动热烈程度可想而知!话不多说,直接上核心代码,机密部分做了伪代码处理publicSeckillActivityRequestVOseckillHandle(SeckillActivityRequestVOrequest){SeckillActivityRequestVOStringkeykey:request。getSeckillId;try{BooleanlockFlagredisTemplate。opsForValue()。setIfAbsent(key,val,10,TimeUnit。SECONDS);if(lockFlag){HTTP请求用户服务进行用户相关的校验用户活动校验库存校验ObjectstockredisTemplate。opsForHash()。get(key:info,stock);assertstock!if(Integer。parseInt(stock。toString())0){业务异常}else{redisTemplate。opsForHash()。increment(key:info,stock,1);生成订单发布订单创建成功事件构建响应VO}}}finally{释放锁stringRedisTemplate。delete(key);构建响应VO}}
  以上代码,通过分布式锁过期时间有效期10s来保障业务逻辑有足够的执行时间;采用tryfinally语句块保证锁一定会及时释放。业务代码内部也对库存进行了校验。看起来很安全啊别急,继续分析事故原因
  飞天茅台抢购活动吸引了大量新用户下载注册我们的APP,其中,不乏很多羊毛党,采用专业的手段来注册新用户来薅羊毛和刷单。当然我们的用户系统提前做好了防备,接入阿里云人机验证、三要素认证以及自研的风控系统等各种十八般武艺,挡住了大量的非法用户。此处不禁点个赞但也正因如此,让用户服务一直处于较高的运行负载中。
  抢购活动开始的一瞬间,大量的用户校验请求打到了用户服务。导致用户服务网关出现了短暂的响应延迟,有些请求的响应时长超过了10s,但由于HTTP请求的响应超时我们设置的是30s,这就导致接口一直阻塞在用户校验那里,10s后,分布式锁已经失效了,此时有新的请求进来是可以拿到锁的,也就是说锁被覆盖了。这些阻塞的接口执行完之后,又会执行释放锁的逻辑,这就把其他线程的锁释放了,导致新的请求也可以竞争到锁这真是一个极其恶劣的循环。这个时候只能依赖库存校验,但是偏偏库存校验不是非原子性的,采用的是getandcompare的方式,超卖的悲剧就这样发生了事故分析
  仔细分析下来,可以发现,这个抢购接口在高并发场景下,是有严重的安全隐患的,主要集中在三个地方:没有其他系统风险容错处理
  由于用户服务吃紧,网关响应延迟,但没有任何应对方式,这是超卖的导火索。看似安全的分布式锁其实一点都不安全
  虽然采用了setkeyvalue〔EXseconds〕〔PXmilliseconds〕〔NXXX〕的方式,但是如果线程A执行的时间较长没有来得及释放,锁就过期了,此时线程B是可以获取到锁的。当线程A执行完成之后,释放锁,实际上就把线程B的锁释放掉了。这个时候,线程C又是可以获取到锁的,而此时如果线程B执行完释放锁实际上就是释放的线程C设置的锁。这就是超卖的直接原因。非原子性的库存校验
  非原子性的库存校验导致在并发场景下,库存校验的结果不准确。这就是超卖的根本原因。
  通过以上分析,问题的根本原因在于库存校验严重依赖了分布式锁。因为在分布式锁正常set、del的情况下,库存校验是没有问题的。但是,当分布式锁不安全可靠的时候,库存校验就没有用了。解决方案
  知道了原因之后,我们就可以对症下药了。实现相对安全的分布式锁
  相对安全的定义:set、del是一一映射的,不会出现把其他现成的锁del的情况。从实际情况的角度来看,即使能做到set、del一一映射,也无法保障业务的绝对安全。因为锁的过期时间始终是有界的,除非不设置过期时间或者把过期时间设置得很长,但这样做也会带来其他问题。故没有意义。要想实现相对安全的分布式锁,必须依赖key的value值。在释放锁的时候,通过value值的唯一性来保证不会误删。我们基于LUA脚本实现原子性的getandcompare,如下:publicvoidsafedUnLock(Stringkey,Stringval){StringluaScriptlocalinARGV〔1〕localcurrredis。call(get,KEYS〔1〕)ifincurrthenredis。call(del,KEYS〔1〕)endreturnOK;RedisScriptStringredisScriptRedisScript。of(luaScript);redisTemplate。execute(redisScript,Collections。singletonList(key),Collections。singleton(val));}
  我们通过LUA脚本来实现安全地解锁。实现安全的库存校验
  如果我们对于并发有比较深入的了解的话,会发现想getandcomparereadandsave这些操作,都是非原子性的。如果要实现原子性,我们也可以借助LUA脚本来实现。但就我们这个例子中,由于抢购活动一单只能下1瓶,因此可以不用基于LUA脚本实现而是基于redis本身的原子性。原因在于:redis会返回操作之后的结果,这个过程是原子性的LongcurrStockredisTemplate。opsForHash()。increment(key,stock,1);
  发现没有,代码中的库存校验完全是画蛇添足。改进之后的代码
  经过以上的分析之后,我们决定新建一个DistributedLocker类专门用于处理分布式锁。publicSeckillActivityRequestVOseckillHandle(SeckillActivityRequestVOrequest){SeckillActivityRequestVOStringkeykey:request。getSeckillId();StringvalUUID。randomUUID()。toString();try{BooleanlockFlagdistributedLocker。lock(key,val,10,TimeUnit。SECONDS);if(!lockFlag){业务异常}用户活动校验库存校验,基于redis本身的原子性来保证LongcurrStockstringRedisTemplate。opsForHash()。increment(key:info,stock,1);if(currStock0){说明库存已经扣减完了。业务异常。log。error(〔抢购下单〕无库存);}else{生成订单发布订单创建成功事件构建响应}}finally{distributedLocker。safedUnLock(key,val);构建响应}}深度思考分布式锁有必要么
  改进之后,其实可以发现,我们借助于redis本身的原子性扣减库存,也是可以保证不会超卖的。对的。但是如果没有这一层锁的话,那么所有请求进来都会走一遍业务逻辑,由于依赖了其他系统,此时就会造成对其他系统的压力增大。这会增加的性能损耗和服务不稳定性,得不偿失。基于分布式锁可以在一定程度上拦截一些流量。分布式锁的选型
  有人提出用RedLock来实现分布式锁。RedLock的可靠性更高,但其代价是牺牲一定的性能。在本场景,这点可靠性的提升远不如性能的提升带来的性价比高。如果对于可靠性极高要求的场景,则可以采用RedLock来实现。再次思考分布式锁有必要么
  由于bug需要紧急修复上线,因此我们将其优化并在测试环境进行了压测之后,就立马开始部署上线了。实际证明,这个优化是成功的,性能方面略微提升了一些,并在分布式锁失效的情况下,没有出现超卖的情况。然而,还有没有优化空间呢?有的!由于服务是集群部署,我们可以将库存均摊到集群中的每个服务器上,通过广播通知到集群的各个服务器。网关层基于用户ID做hash算法来决定请求到哪一台服务器。这样就可以基于应用缓存来实现库存的扣减和判断。性能又进一步提升了!通过消息提前初始化好,借助ConcurrentHashMap实现高效线程安全privatestaticConcurrentHashMapLong,BooleanSECKILLFLAGMAPnewConcurrentHashMap();通过消息提前设置好。由于AtomicInteger本身具备原子性,因此这里可以直接使用HashMapprivatestaticMapLong,AtomicIntegerSECKILLSTOCKMAPnewHashMap();。。。publicSeckillActivityRequestVOseckillHandle(SeckillActivityRequestVOrequest){SeckillActivityRequestVOLongseckillIdrequest。getSeckillId();if(!SECKILLFLAGMAP。get(requestseckillId)){业务异常}用户活动校验库存校验if(SECKILLSTOCKMAP。get(seckillId)。decrementAndGet()0){SECKILLFLAGMAP。put(seckillId,false);业务异常}生成订单发布订单创建成功事件构建响应}
  通过以上的改造,我们就完全不需要依赖redis了。性能和安全性两方面都能进一步得到提升!当然,此方案没有考虑到机器的动态扩容、缩容等复杂场景,如果还要考虑这些话,则不如直接考虑分布式锁的解决方案。总结
  稀缺商品超卖绝对是重大事故。如果超卖数量多的话,甚至会给平台带来非常严重的经营影响和社会影响。经过本次事故,让我意识到对于项目中的任何一行代码都不能掉以轻心,否则在某些场景下,这些正常工作的代码就会变成致命杀手!对于一个开发者而言,则设计开发方案时,一定要将方案考虑周全。怎样才能将方案考虑周全?唯有持续不断地学习!
  PS:如果觉得我的分享不错,欢迎大家随手点赞、在看
投诉 评论

沃尔沃计划投资100亿瑞典克朗在瑞典生产下一代纯电动车中新财经2月9日电(记者彭大伟)沃尔沃汽车8日表示,其将在未来数年内于其位于瑞典Torslanda的制造工厂投资100亿瑞典克朗,为生产下一代纯电动汽车做好准备。沃尔沃汽……C语言判断这一天是该年的第几天includestdio。hincludestdlib。htypedefstructData{intday……江淮汽车回应与华为合作造车传闻目前没有,未来不排除更多可能性张兴旺中国证券报中证网中证网讯(记者张兴旺)近日,有传闻称,江淮汽车将与华为合作造车,12月28日上午,中国证券报记者致电江淮汽车证券部求证。江淮汽车证券部工作人员对中国……外媒评年度十佳手机,苹果评价最好上榜最多,仅有一款国产机上榜随着各大厂商秋季发布会的召开,许多国内外的博主开始选出2021年的年度最佳手机榜单。国外知名数码科技媒体ZDnet在近日公布了2021年10款最佳智能手机榜单,评选内容包含5G……有哪些智能的培训系统推荐?现在市面上的教培系统比较多,大家要根据自身的规模,具体的需求功能,还有价格选择。一般系统都会有学员档案管理、班级管理、上课考勤、课消统计,还有一些有员工管理、财务管理,系……移动,想说用你不容易三大运营商都使用过,联通只买过免月租的流量卡,现在早不使用了。但是我使用了20多年的移动给我的感觉真是不好。事情是这样的,岳母去年搬家要装宽带,我将8元一个月的移动保号套……Redis分布式锁使用不当,酿成一个重大事故,超卖了100瓶来源:juejin。cnpost6854573212831842311基于Redis使用分布式锁在当今已经不是什么新鲜事了。本篇文章主要是基于我们实际项目中因为re……纯电续航超100公里的奔驰GLE350e,已完全克服了换挡顿提起豪车品牌,很多人都会想起奔驰,即便是BBA三家品牌中,奔驰品牌力也是数一数二的,旗下有不少车型,有的走的是运动范,有的走的是商务范,很显然GLE就是运动范,大众对于这款车应……芯片受困却破局而出,鸿蒙重启华为,任正非这次能赢吗?当全世界都认为华为即将按下‘暂停键’的时候,华为选择按下‘开始键’。这是去年华为开发者大会上的一句宣言,在接连遇到来自国外的压力后,华为竟然还能拍拍胸脯表示不惧,不由得让……新款雅阁正式上市,内外升级明显,增加黑幻夜版,值得考虑吗?日系三剑客要数谁最能吸引年轻人的注意,在我心里非本田车系莫属,逐渐年轻化的外观,内饰也是逐渐现代化,秒天秒地秒空气的思域一直是网红般的存在,而它的兄弟雅阁也是居家与运动两不误。……请问几大笔记本电脑品牌到底哪个更靠谱?联想,华硕,戴尔,惠普ASUS老奸巨猾,一线品牌,高端游戏本一线模样,低端笔记本欺骗消费者,而且个人认为专门欺骗对笔记本了解不足的小白。天选今年的CPU性能释放不错,但是其他硬件配置是不不拆机不知道……儿子秋天上学,学校让买i7的电脑,学的是动漫制作专业,拯救者先说结论:联想拯救者y7000p值得大学新生买,但目前这个节点我更推荐12代酷睿版本,性能提升很多,外观设计也更有味儿!y7000p即使电竞本也是性能本联想的拯救者……
魅蓝回归首款手机亮相!5000mAh大电池加持,699起售8G的iphone4s目前还有什么用?芯片航母重整旗鼓,中国缺芯难题能否柳暗花明?苹果停止在俄罗斯销售手机,小米为首的国产品牌,或成大赢家?郑州不少市民扫码变红,咋解决?海思麒麟我还行py3nvml实现GPU相关信息读取任嘉伦还能持续火多久?A股万亿赛道启动储能板块里的十二大潜力优质龙头股壹号掌机OnexPlayer1S8。4英寸玩游戏,比笔记本电vivoX70系列曝光!搭载多平台处理器,最高支持55W快充小米红米Note10Pro怎么样?关于中秋节作文再遇中秋南海填岛采取的什么方式号手就位收官,梁诺送给夏拙的箱子揭示了两人的结局警惕包括硝苯地平片在内的这5种降压药已经被淘汰了,慎用一个令我感动的人职场名字好读人缘好更容易升职汽车导航仪有必要装吗科学家研制全新可变色玻璃,可有效节能献给我的母亲实用父亲节的作文300字集合五篇微信怎么找回来微信如何找回来家乡的小河六年级作文范文

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找七猫云易事利