文章目录
- 什么是超卖问题?
- 推荐策略:Redis原子操作(Redis incr)+乐观锁+lua脚本
- 利用Redis increment 的原子操作,保证库存数安全
- update使用乐观锁
- LUA脚本保持库存原子性
什么是超卖问题?
在并发的场景下,比如商城售卖商品中,一件商品的销售数量>库存数量的问题,称为超卖问题。主要原因是在并发场景下,请求几乎同时到达,对库存资源进行竞争,由于没有适当的并发控制策略导致的错误。
推荐策略:Redis原子操作(Redis incr)+乐观锁+lua脚本
利用Redis increment 的原子操作,保证库存数安全
- 先查询redis缓存中是否有库存信息,如果没有就去数据库查,这样就可以减少访问数据库的次数。获取到后把数值填入redis,以商品id为key,数量为value。 还需要设置redis对应这个key的超时时间,以防所有商品库存数据都在redis中。
- 比较下单数量的大小,如果够就做后续逻辑。
- 执行redis客户端的increment,参数为负数,则做减法。因为redis是单线程处理,并且因为increment让key对应的value减少后返回的是修改后的值。有的人会不做第一步查询直接减,其实这样不太好,因为当库存为1时,很多做减3,或者减30情况,其实都是不够,这样就白减。
- 扣减数据库的库存,这个时候就不需要再select查询,直接乐观锁update,把库存字段值减1 。
- 做完扣库存就在订单系统做下单。
update使用乐观锁
updateProduct方法中执行的sql如下:
update Product set count = count - 购买数量 where id = 商品id and count - 购买数量 >= 0 and version = 查到的version;
虽然redis已经防止了超卖,但是数据库层面,为了也要防止超卖,以防redis崩溃时无法使用或者不需要redis处理时,则用乐观锁,因为不一定全部商品都用redis。
利用sql每条单条语句都是有事务的,所以两条sql同时执行,也就只会有其中一条sql先执行成功,另外一条后执行。
LUA脚本保持库存原子性
扣减redis的库存时,最好用lua脚本处理,因为如果剩余1个时,用户买100个,这个时候其实会先把key increase -100就会变负99。
所以用lua脚本先查询数量剩余多少,是否够减100后,再去减100。
本人水平有限,有错的地方还请批评指正。
什么是精神内耗?
简单地说,就是心理戏太多,自己消耗自己。
所谓:
言未出,结局已演千百遍;
身未动,心中已过万重山;
行未果,假想灾难愁不展;
事已闭,过往仍在脑中演。