每秒上千订单场景下的分布式锁高并发优化实践!【石杉的架构笔记】

欢迎关注我的公众号:石杉的架构笔记(ID:shishan100)java

周一至周五早8点半!精品技术文章准时送上!面试


上一篇文章咱们聊了聊Redisson这个开源框架对Redis分布式锁的实现原理,若是有不了解的兄弟能够看一下: 拜托,面试请不要再问我Redis分布式锁的实现原理


今天就给你们聊一个有意思的话题:每秒上千订单场景下,如何对分布式锁的并发能力进行优化?redis

背景引入

首先,咱们一块儿来看看这个问题的背景?算法

前段时间有个朋友在外面面试,而后有一天找我聊说:有一个国内不错的电商公司,面试官给他出了一个场景题:数据库

假以下单时,用分布式锁来防止库存超卖,可是是每秒上千订单的高并发场景,如何对分布式锁进行高并发优化来应对这个场景?

他说他当时没答上来,由于没作过没什么思路。其实我当时听到这个面试题内心也以为有点意思,由于若是是我来面试候选人的话,应该会给的范围更大一些。性能优化

好比,让面试的同窗聊一聊电商高并发秒杀场景下的库存超卖解决方案,各类方案的优缺点以及实践,进而聊到分布式锁这个话题。架构

由于库存超卖问题是有不少种技术解决方案的,好比悲观锁,分布式锁,乐观锁,队列串行化,Redis原子操做,等等吧。并发

可是既然那个面试官兄弟限定死了用分布式锁来解决库存超卖,我估计就是想问一个点:在高并发场景下如何优化分布式锁的并发性能。框架

我以为,面试官提问的角度仍是能够接受的,由于在实际落地生产的时候,分布式锁这个东西保证了数据的准确性,可是他自然并发能力有点弱。iphone

恰好我以前在本身项目的其余场景下,确实是作太高并发场景下的分布式锁优化方案,所以正好是借着这个朋友的面试题,把分布式锁的高并发优化思路,给你们来聊一聊。

库存超卖现象是怎么产生的?

先来看看若是不用分布式锁,所谓的电商库存超卖是啥意思?你们看看下面的图:

这个图,其实很清晰了,假设订单系统部署两台机器上,不一样的用户都要同时买10台iphone,分别发了一个请求给订单系统。

接着每一个订单系统实例都去数据库里查了一下,当前iphone库存是12台。

俩大兄弟一看,乐了,12台库存大于了要买的10台数量啊!

因而乎,每一个订单系统实例都发送SQL到数据库里下单,而后扣减了10个库存,其中一个将库存从12台扣减为2台,另一个将库存从2台扣减为-8台。

如今完了,库存出现了负数!泪奔啊,没有20台iphone发给两个用户啊!这可如何是好。

用分布式锁如何解决库存超卖问题?

咱们用分布式锁如何解决库存超卖问题呢?其实很简单,回忆一下上次咱们说的那个分布式锁的实现原理:

同一个锁key,同一时间只能有一个客户端拿到锁,其余客户端会陷入无限的等待来尝试获取那个锁,只有获取到锁的客户端才能执行下面的业务逻辑。

代码大概就是上面那个样子,如今咱们来分析一下,为啥这样作能够避免库存超卖?

你们能够顺着上面的那个步骤序号看一遍,立刻就明白了。

从上图能够看到,只有一个订单系统实例能够成功加分布式锁,而后只有他一个实例能够查库存、判断库存是否充足、下单扣减库存,接着释放锁。

释放锁以后,另一个订单系统实例才能加锁,接着查库存,一下发现库存只有2台了,库存不足,没法购买,下单失败。不会将库存扣减为-8的。

有没有其余方案能够解决库存超卖问题?

固然有啊!好比悲观锁,分布式锁,乐观锁,队列串行化,异步队列分散,Redis原子操做,等等,不少方案,咱们对库存超卖有本身的一整套优化机制。

可是前面说过了,这篇文章就聊一个分布式锁的并发优化,不是聊库存超卖的解决方案,因此库存超卖只是一个业务场景而已

之后有机会笔者会写一篇文章,讲讲电商库存超卖问题的解决方案,这篇文章先focus在一个分布式锁并发优化上,但愿你们明白这个用意和背景,避免有的兄弟没看清楚又吐槽。

并且建议你们即便对文章里的内容有异议,公众号后台给我留言跟我讨论一下,技术,就是要多交流,打开思路,碰撞思惟。

分布式锁的方案在高并发场景下

好,如今咱们来看看,分布式锁的方案在高并发场景下有什么问题?

问题很大啊!兄弟,不知道你看出来了没有。分布式锁一旦加了以后,对同一个商品的下单请求,会致使全部客户端都必须对同一个商品的库存锁key进行加锁。

好比,对iphone这个商品的下单,都必对“iphone_stock”这个锁key来加锁。这样会致使对同一个商品的下单请求,就必须串行化,一个接一个的处理。

你们再回去对照上面的图反复看一下,应该能想明白这个问题。

假设加锁以后,释放锁以前,查库存 -> 建立订单 -> 扣减库存,这个过程性能很高吧,算他全过程20毫秒,这应该不错了。

那么1秒是1000毫秒,只能容纳50个对这个商品的请求依次串行完成处理。

好比一秒钟来50个请求,都是对iphone下单的,那么每一个请求处理20毫秒,一个一个来,最后1000毫秒正好处理完50个请求。

你们看一眼下面的图,加深一下感受。

因此看到这里,你们起码也明白了,简单的使用分布式锁来处理库存超卖问题,存在什么缺陷。

缺陷就是同一个商品多用户同时下单的时候,会基于分布式锁串行化处理,致使无法同时处理同一个商品的大量下单的请求。

这种方案,要是应对那种低并发、无秒杀场景的普通小电商系统,可能还能够接受。

由于若是并发量很低,每秒就不到10个请求,没有瞬时高并发秒杀单个商品的场景的话,其实也不多会对同一个商品在一秒内瞬间下1000个订单,由于小电商系统没那场景。

如何对分布式锁进行高并发优化?

好了,终于引入正题了,那么如今怎么办呢?

面试官说,我如今就卡死,库存超卖就是用分布式锁来解决,并且一秒对一个iphone下上千订单,怎么优化?

如今按照刚才的计算,你一秒钟只能处理针对iphone的50个订单。

其实说出来也很简单,相信不少人看过java里的ConcurrentHashMap的源码和底层原理,应该知道里面的核心思路,就是分段加锁

把数据分红不少个段,每一个段是一个单独的锁,因此多个线程过来并发修改数据的时候,能够并发的修改不一样段的数据。不至于说,同一时间只能有一个线程独占修改ConcurrentHashMap中的数据。

另外,Java 8中新增了一个LongAdder类,也是针对Java 7之前的AtomicLong进行的优化,解决的是CAS类操做在高并发场景下,使用乐观锁思路,会致使大量线程长时间重复循环。

LongAdder中也是采用了相似的分段CAS操做,失败则自动迁移到下一个分段进行CAS的思路。

其实分布式锁的优化思路也是相似的,以前咱们是在另一个业务场景下落地了这个方案到生产中,不是在库存超卖问题里用的。

可是库存超卖这个业务场景不错,很容易理解,因此咱们就用这个场景来讲一下。你们看看下面的图:

其实这就是分段加锁。你想,假如你如今iphone有1000个库存,那么你彻底能够给拆成20个库存段,要是你愿意,能够在数据库的表里建20个库存字段,好比stock_01,stock_02,相似这样的,也能够在redis之类的地方放20个库存key。

总之,就是把你的1000件库存给他拆开,每一个库存段是50件库存,好比stock_01对应50件库存,stock_02对应50件库存。

接着,每秒1000个请求过来了,好!此时其实能够是本身写一个简单的随机算法,每一个请求都是随机在20个分段库存里,选择一个进行加锁。

bingo!这样就行了,同时能够有最多20个下单请求一块儿执行,每一个下单请求锁了一个库存分段,而后在业务逻辑里面,就对数据库或者是Redis中的那个分段库存进行操做便可,包括查库存 -> 判断库存是否充足 -> 扣减库存。

这至关于什么呢?至关于一个20毫秒,能够并发处理掉20个下单请求,那么1秒,也就能够依次处理掉20 * 50 = 1000个对iphone的下单请求了。

一旦对某个数据作了分段处理以后,有一个坑你们必定要注意:就是若是某个下单请求,咔嚓加锁,而后发现这个分段库存里的库存不足了,此时咋办?

这时你得自动释放锁,而后立马换下一个分段库存,再次尝试加锁后尝试处理。这个过程必定要实现。

分布式锁并发优化方案有没有什么不足?

不足确定是有的,最大的不足,你们发现没有,很不方便啊!实现太复杂了。

  • 首先,你得对一个数据分段存储,一个库存字段原本好好的,如今要分为20个分段库存字段;
  • 其次,你在每次处理库存的时候,还得本身写随机算法,随机挑选一个分段来处理;
  • 最后,若是某个分段中的数据不足了,你还得自动切换到下一个分段数据去处理。

这个过程都是要手动写代码实现的,仍是有点工做量,挺麻烦的。

不过咱们确实在一些业务场景里,由于用到了分布式锁,而后又必需要进行锁并发的优化,又进一步用到了分段加锁的技术方案,效果固然是很好的了,一会儿并发性能能够增加几十倍。

该优化方案的后续改进

以咱们本文所说的库存超卖场景为例,你要是这么玩,会把本身搞的很痛苦!

再次强调,咱们这里的库存超卖场景,仅仅只是做为演示场景而已,之后有机会,再单独聊聊高并发秒杀系统架构下的库存超卖的其余解决方案。

上篇文章的补充说明

本文最后作个说明,笔者收到一些朋友留言,说有朋友在技术群里看到上篇文章以后,吐槽了一通上一篇文章(拜托,面试请不要再问我Redis分布式锁的实现原理),说是那个Redis分布式锁的实现原理把人给带歪了。

在这儿得郑重说明一下,上篇文章,明确说明了是Redisson那个开源框架对Redis锁的实现原理,并非我我的YY出来的那一套原理。

实际上Redisson做为一款优秀的开源框架,我以为他总体对分布式锁的实现是OK的,虽然有一些缺陷,可是生产环境可用。

另外,有的兄弟可能以为那个跟Redis官网做者给出的分布式锁实现思路不一样,因此就吐槽,说要遵循Redis官网中的做者的分布式锁实现思路。

其实我必须指出,Redis官网中给出的仅仅是Redis分布式锁的实现思路而已,记住,那是思路!思路跟落地生产环境的技术方案之间是有差距的。

好比说Redis官网给出的分布式锁实现思路,并无给出到分布式锁的自动续期机制、锁的互斥自等待机制、锁的可重入加锁与释放锁的机制。可是Redisson框架对分布式锁的实现是实现了一整套机制的。

因此重复一遍,那仅仅是思路,若是你愿意,你彻底能够基于Redis官网的思路本身实现一套生产级的分布式锁出来。

另外Redis官网给出的RedLock算法,一直是我我的并不推崇在生产使用的。

由于那套算法中可能存在一些逻辑问题,在国外是引起了争议的,连Redis做者本身都在官网中给出了由于他的RedLock算法而引起争议的文章,固然他本身是不太赞成的。

可是这个事儿,就搞成公说公有理,婆说婆有理了。具体请参加官网原文:

Martin Kleppmann analyzed Redlock here. I disagree with the analysis and posted my reply to his analysis here。

所以下回有机会,我会经过大量手绘图的形式,给你们写一下Redis官方做者本身提出的RedLock分布式锁的算法,以及该算法基于Redisson框架如何落地生产环境使用,到时你们能够再讨论。


End


若有收获,请帮忙转发,您的鼓励是做者最大的动力,谢谢!

一大波微服务、分布式、高并发、高可用原创系列

文章正在路上,欢迎扫描下方二维码,持续关注:


石杉的架构笔记(id:shishan100)

十余年BAT架构经验倾囊相授


推荐阅读:

一、拜托!面试请不要再问我Spring Cloud底层原理

二、【双11狂欢的背后】微服务注册中心如何承载大型系统的千万级访问?

三、【性能优化之道】每秒上万并发下的Spring Cloud参数优化实战

四、微服务架构如何保障双11狂欢下的99.99%高可用

五、兄弟,用大白话告诉你小白都能听懂的Hadoop架构原理

六、大规模集群下Hadoop NameNode如何承载每秒上千次的高并发访问

七、【性能优化的秘密】Hadoop如何将TB级大文件的上传性能优化上百倍

八、拜托,面试请不要再问我TCC分布式事务的实现原理坑爹呀!

九、【坑爹呀!】最终一致性分布式事务如何保障实际生产中99.99%高可用?

十、拜托,面试请不要再问我Redis分布式锁的实现原理!

十一、【眼前一亮!】看Hadoop底层算法如何优雅的将大规模集群性能提高10倍以上?

相关文章
相关标签/搜索