高并发,业务量大的业务场景下,数据库减库存的解决方案

一,用户下单购买商品的情况下,如果有多个人同时下单,减除库存的情况下,如果遇到了减去库存的并发问题,这个时候应该怎么处理呢?

传统的业务流程场景下,处理流程是这样的:
1,库存查询,通过dao查询商品库存,返回库存数量
select stock from goods where gid=#{gid}
2,逻辑判断库存是否充足,充足进行库存减扣
update stock set stock=stock-#{count} where gid=#{gid}


但是在减去库存的时候往往会出现一些意想不到的错误,出错后程序设计可能会有“重试”机制,但是在重试时,可能会得到错误的数据,导致重复减扣或者库存为负数。

重试导致错误的根本原因,是因为“扣减”操作是一个非幂等的操作,不能够重复执行,改成设置操作则不会有这个问题,如下:假如用户购买三件商品,通过数据库查询,返回商品余量为5,然后通过逻辑计算减去三件商品余2,然后在设置数量2到数据库持久化
1,库存查询,通过dao查询商品库存,假如返回库存数量为5
select stock from goods where gid=#{gid}
2,判断库存是否充足,充足进行逻辑库存减扣,设置数据库(逻辑计算5-3=2,new_stock=2)
update goods set stock=#{new_stock} where gid=#{gid}

这样虽然避免了程序出错,“重试”机制的减扣没有导致数据出现错误,但是如果用户发生了并发的购买动作(秒杀类业务特别容易出现),流程如下:
用户A和用户B并发购买商品,此时同时查询数据库库存数量为5
用户A购买了3个库存,于是库存要设置为2
用户B购买了2个库存,于是库存要设置为3

此时场景,应该是五个商品刚好售卖完,储存应该为0,但是这两个设置库存的接口并发执行,库存会先变成2,再变成3,导致数据不一致(实际卖出了5件商品,但库存只扣减了2,最后一次设置库存会覆盖和掩盖前一次并发操作)
其根本原因是,设置操作发生的时候,没有检查实际库存与查询出来的库存有没有变化,理论上:

库存为5时,用户A的库存设置才能成功
库存为5时,用户B的库存设置才能成功

实际执行的时候:
库存为5,用户A的set stock=2确实应该成功
库存变为2了,用户B的set stock=3应该失败掉

所以,在数据量较大的情况下,减库存应该是以下这种方案:
update goods set stock=#{new_stock} where gid=#{gid}
修改为:
update goods set stock=#{new_stock} where gid=#{gid} and stock=#{old_stock}
这样修改后,用户A设置库存为2了,用户B再去设置库存,通过条件stock=#{old_stock}判断条件已经不满足了更新失败,从而保证了数据的一致性。

总结:
在业务复杂,数据量大,并发量大的情况下,库存扣减容易引发数据的不一致,常见的优化方案有两个:

  • 调用“设置库存”接口,能够保证数据的幂等性
  • 在实现“设置库存”接口时,需要加上原有库存的比较,才允许设置成功,能解决高并发下库存扣减的一致性问题

你可能感兴趣的:(mysql,高并发)