最近在做公司WMS库存模块的需求,所以网上查阅了一些库存扣减相关的文章学习了一下。
方案
我们先来看看都有哪些扣减方案,各自优缺点给
方案一:查出库存,然后减去扣减数量,再更新回数据库
select stock_num from table where stock_id = 5;
new_stock_num = stock_num - $reduce;
update table set stock_num = new_stock_num where stock_id = 5;
这个方案大家应该都是直到是有问题的,所有操作不是原子性的。
方案二:改进方案一,更新时加上查询查询时的库存数量
select stock_num from table where stock_id = 5;
new_stock_num = stock_num - $reduce;
update table set stock_num = new_stock_num where stock_id = 5 and stock_num = stock_num;
这种方案类似一个乐观锁,只有更新时库存数量和查询时的库存数量相等才能更新。
这个方案虽然操作不具有原子性,但却是可以保证线程安全,生产环境中可以使用这种方法。
但是缺点就是如果并发量很大,那会存在非常多更新失败的情况。
方案三:用一条SQL来实现扣减库存
update table set stock_num = stock_num - $reduce where id = 5 and stock_num >= $reduce;
这样一条SQL就能解决库存扣减问题,不会出现并发问题,生产环境推荐使用此方案。
但是也有缺点:就是如果上游业务因为触发重试,会导致多扣库存的情况,所以需要做幂等校验,防止库存多扣的情况。
方案四:通过事务和MySQL的锁来解决
开启事务
查询时使用for update来将查询的数据加上锁,让其他事务中不可读。
select stock_num from table where id = 5 for update
然后计算数量更新库存
提交事务
这种方式也是可以使用的,但是在分布式事务的情况下就不能使用这种方式了,而且性能极低。
方案五:使用Redis,利用Redis的原子性减操作
decr
命令可以实现原子操作,如果扣减结果为负数了会扣减失败。
使用Redis系统的复杂度就提升了,要考虑很多情况了,如:Redis挂了怎么办,数据一致性问题等等。
只使用数据库来扣减库存,肯定是扛不住非常多的并发的,所以如果并发量非常大的情况,是必须要使用redis的,虽然会带来许多问题,但是也是有解决方案的。
因为我此次所做的库存系统是内部系统,没有很大的并发量,所以就没有研究使用Redis来扣减库存的方案,大家如果遇到了可自行研究一番。