场景描述:某电商需要对几款商品创建一个秒杀促销,每笔订单只可购买一件商品。
流程图
设计思路
-
简单实现主流程,完成商品下单
数据库设计:商品表(product_info)订单表(order_info)
service层实现:
- 下单操作
- 校验库存 :获取商品sale_num+sku_num 小于等于 stock_num数量
- 扣减库存 :sale_num+sku_num且不大于stock_num,修改库存数量(update product_info set sale_num = ? where id =?)
- 下单完成:记录订单数据到订单表
这样的流程,看似是一个很完整的下单流程,当发生大量并发操作时,可能同时有多个线程查询到当前sale_num<stock_num,然后判断下单成功,我们会查询到订单表里生成的订单数据大于库存数据,就会出现超卖情况
解决超卖的一种简单方式就是增加乐观锁(乐观锁不会对数据上锁,其他请求仍可访问数据,悲观所是给数据上锁独占,乐观锁的原理简单说可以理解为版本号,例如:线程请求时会得到这条数据的version=1.0,然后对version=1.0的数据做修改操作,同时修改版本号 version=1.1并提交;当A、B两个线程同时访问统一条数据,同时获得数据且版本号为1.0,当A线程先完成了修改操作,对版本号为1.0的数据做了修改,并修改了版本号为1.1后提交,这时B线程完成处理逻辑,要去修改手里版本好为1.0的数据时,已经查不到了这条数据,修改失败,锁生效)
2.增加锁机制
ALTER TABLE `product_info`
ADD COLUMN `version` BIGINT(20) NOT NULL DEFAULT 1 ;
- 下单操作
- 校验库存 :获取商品sale_num+sku_num < stock_num数量,且获取version
- 扣减库存 :sale_num+sku_num 不大于stock_num,修改库存数量(update product_info set sale_num = ? ,version = oldVersion+1 where id =? and version=oldVersion)
- 下单完成:记录订单数据到订单表
当发生大并发时,多个线程同时获取到满足下单的商品 version ,A线程先完成update操作,提交事务,下单成功,并对version+1,此时B线程执行update操作,查询不到匹配的version商品,修改库存失败,下单失败