hello,咱们好,我是张张,「架构精进之路」公号作者。

前语:

随着中国消费认知的不断晋级,网购走进千家万户,越来越被人们所接受。淘宝、唯品会、考拉、京东、拼多多等逐渐成为咱们生活的重要组成部分。

除了惯例的购物下单外,这些电商渠道还常常搞一些双十一活动,秒杀、大促、限时购,各种营销玩法,层出不穷! 今天就来跟咱们聊一聊电商技能里的库存扣减。

并发减库存

秒杀的场景有许多,比方:抢购、抢票、抢红包等等。总之,便是在极短时间内有大量的恳求。

咱们都知道,这种体系规划的大方向便是限流,即通过层层过滤,终究只让相对较少的恳求进入到中心业务处理层。

这儿不谈秒杀规划,不谈运用行列等使恳求串行化,就谈下怎么用锁来确保数据正确,便是现已到减库存那一步了,在这一步中假如确保不超卖。

用行列的话,能够是Java主动的行列,也能够用Redis的LPUSH RPOP

重点是扣减库存

我了解,首要的办法是加锁。加锁有两个层面:一个是程序层面,另一个是数据库层面。

电商并发减库存设计,如何做到不超卖

分布式锁

这种场景下应该很少有人用Java自带的锁(比方:synchronized、Lock)吧,因为它们只在同一个JVM内有效,假如你的使用部署了多台的话,应该用分布式锁。

关于Redis分布式锁,能够看我之前的一篇《根据Redis的分布式锁的简略完成》

其实,这儿加分布式锁便是将多线程恳求转成单线程恳求,因为每次只有一个线程取得锁并履行,其他都被堵塞了。

这儿有一点需要留意,便是当你使用了业务的话或许会存在问题,请看下面的代码

电商并发减库存设计,如何做到不超卖

或许有人会这样写,第一眼看起来挺好的,没问题啊,但细心实践证明是有问题的。

咱们知道,mysql默许的业务阻隔等级是REPEATABLE-READ

关于业务阻隔等级这块儿,可参阅《mysql业务阻隔等级》

在这种阻隔等级下,同一个业务中屡次读取,回来的数据是相同的

同时,Spring声明式业务默许的传达特性REQUIRED

电商并发减库存设计,如何做到不超卖

Spring声明式业务是Spring AOP最好的例子,Spring是通过AOP署理的办法来完成业务的,也便是说在调用reduceStock()办法的之前就现已敞开了业务。

那么,在并发状况下或许会存在这样的状况,假定线程T1和T2都履行到这儿,所以它们都敞开了业务S1和S2,T1先履行,T2后履行,

因为T2履行的时分业务现已创建了,根据阻隔等级,这个时分业务S2读取不到S1已提交的数据,所以就会呈现T1和T2读取到的值是相同的,即T2读取的是T1更新前的库存数据。

关于这一点,咱们能够自己写个代码测验一下,下面是一段参阅:

电商并发减库存设计,如何做到不超卖

鉴于这种状况呢,能够将库存放到Redis中,咱们直接读写Redis,这样能够避免受数据库业务的影响,当然这也会带来新的问题,不再评论。

数据库达观锁

CAS(compare and swap)比较并交流

在Java中,一个线程想修正某个变量的值,那么第一步是将变量的值从主内存中读取到自己作业内存中,然后修正,最终写回主内存。这个过程能够归结为:读取——修正——写入,在写回内存的时分或许当时内存中那个值现已发生了改变,这个时分假如持续写则会覆盖他人的数据,只有当内存中的那个值和它修正之前读到的那个值相同,才干够写入。这个跟数据库是相同的。Java中通过Unsafe中compareAndSwapObject这样的办法类完成的,它直接调用CPU指令。

电商并发减库存设计,如何做到不超卖

数据库中也有CAS,达观锁便是一种CAS

经典的达观锁完成:

数据添加一个版别标识,一般是通过为数据库表添加一个数字类型的 “version” 字段来完成。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当咱们提交更新的时分,判断数据库表对应记载的当时版别信息与第一次取出来的version值进行比对,假如数据库表当时版别号与第一次取出来的version值相等,则予以更新,不然认为是过期数据。

更新的时分带上版别号,只有当时版别号与更新之前查询时的版别一致,才会更新

电商并发减库存设计,如何做到不超卖

ABA问题

这儿趁便多提一句,CAS中的ABA问题

假定,原先的值是A,线程-1读取到的值是A,想把它改成D,可是在此期间,有或许其他线程现已屡次修正过这个值,只不过最终当线程-1准备将A改成D的时分,它发现刚好还是A,以为没有人改正,其实这时分的A现已不是本来的A了。

也便是说,尽管修正之前做了比较,当然,仍然会呈现如下状况:

电商并发减库存设计,如何做到不超卖

发生原因

ABA问题导致的原因,是CAS过程中只简略进行了“值”的校验,有些状况下,“值”虽然相同,却现已不是本来的数据了。

优化方向

CAS不能只比对“值”,还必须确保的是本来的数据,才干修正成功。

常见实践

“版别号”的比对,一个数据一个版别,版别改变,即使值相同,也不应该修正成功。

不只要关注值,还要关注是不是本来的对象

电商并发减库存设计,如何做到不超卖

根据“值”的CAS达观锁,或许导致ABA问题。CAS达观锁,必须确保修正时的“此数据”便是“彼数据”,应该由“值”比对,优化为“版别号”比对。

电商并发减库存设计,如何做到不超卖

参阅:

www.cnblogs.com/cjsblog/p/9…

希望今天的讲解对咱们有所协助,谢谢!

Thanks for reading!

作者:架构精进之路,十年研制风雨路,大厂架构师,CSDN 博客专家,专注架构技能沉淀学习及分享,工作与认知晋级,坚持分享接地气儿的干货文章,期待与你一起生长。
关注并私信我回复“01”,送你一份程序员生长进阶大礼包,欢迎勾搭。