Mysq详细讲解如何解决库存并发问题
面临的问题
长话短说,假设我们现在面临以下需求
- 商品的库存有两千,卖完为止
- 某商品本日的售卖只允许卖出一百,卖完为止
如何实现
我提出的方案也很简单,使用乐观锁的方式。
以下是具体的方案
-- stock: 当前库存数 number:扣减的数量 -- UPDATE t SET stock -= number WHERE stock >= number
外加上事务,便可以实现一个基本的库存扣减操作。大部分情况下,无需担心所谓的“并发问题”。事务具有的特性,会在此处帮你解决的掉这个大难题。
(简单的说:事务会在执行 非查询 的操作的时候,会实现类似锁的功能。直到前面的事物提交或者回滚之前,后续的操作都会被挂住)
需求具体实现的方案
1.商品的库存两千,卖完为止
其实从理论上,想解决这个问题,只依靠上文之中的update语句便可以完成。
具体步骤如下:
- 执行UPDATE 语句,查看其结果 。
- 若是,则执行后续操作
- 若否,代码回滚
具体代码如下
//开始事务 beginTransaction(); // 扣减库存前的业务 // 执行扣减库存操作 boolean reduceStockSuccess = reduceStock(); if(!reduceStockSuccess){ //扣减库存失败,代码回滚 rollback(); return; } // 执行扣减库存后的业务操作 //记录库存 writeRecod(); //提交事务 commit(); return;
虽然,程序其实如此便可。但是从个人的角度去看,我还是建议大家多做一点校验,以减少UPDATE程序运行次数。
就比如说,我们可以在前面加一个查询当前库存数量代码。
主要的目的在于,虽然这个校验代码,不能说百分百的解决问题 ,挡住所有的流量。但是却可以挡住大部分无意义的流量,调用UPDATE的次数。
简单来说,就跟我们小时候玩坦克大战一样,虽然我们玩家不能挡住所有的进攻者。但也并不是随随便便谁都可以往我们家基地开炮。
//开始事务 beginTransaction(); // 扣减库存前的业务 int stock = getStock(); if(stock <= 0 ){ //库存不足,退出程序 rollback(); return; } // 执行扣减库存操作 boolean reduceStockSuccess = reduceStock(); if(!reduceStockSuccess){ //扣减库存失败,代码回滚 rollback(); return; } // 执行扣减库存后的业务操作 //记录库存 writeRecod(); //提交事务 commit(); return;
2 . 日库存数一百 , 卖完为止
该需求相比1来说,问题在于。单从库存表,记录表来说。除非我们在新增到记录表的SQL里面将每日库存数100接入。否则,我们无法通过事务与SQL来帮我们解决并发问题。
但是我们又不可能在记录购买信息的代码内,每日库存数100的逻辑耦合进去。
因此,这个问题对于MYSQL来说是个死局。
若想单靠MYSQL之力若想破局,我们只能依靠将每日库存数的这个逻辑,专门设计一张数据表。
如设计一张商品每日购买数量记录表记录某个商品每日被购买的数量。
在每次购买的时候都更新一下本日购买的数量
UPDATE day_t SET day_stock += number WHERE day_stock + number <= 100
虽然能解决需求问题,但是表现出来的问题依然是业务耦合进公关表内。
- 每日购买数量表,不应当仅仅为每日购买上限服务。当前的解决问题的方案,等同于解决问题的同时又制造了一个差不多的问题。只是一个问题转移到了其他部分出问题
- 即使我们为该需求专门创建一张表,但是随着后续的数据表越来越多,程序的管理性却越来越差,比如后续还会出现周限制,月限制,年限制等等等等。
总结
现在我们可以看出,使用MYSQL的方式虽然简单,但是却有着非常大的局限性。
但这里也并不是说某些方案一定好,却也一定差。
主要看当前的业务,MYSQL的方式,适用于最底层。适合完成某个业务最原始的功能。