问题描述:ERP系统业务方反馈调取修改商城库存接口后,商城库存没有改变,需要验证修改库存和扣减库存并发执行时,是否发生了覆盖,导致数据不一致的问题。
1、测试demo
从代码分析,扣减库存,使用的是悲观锁。
@RestController
@RequestMapping(value = "testreducestorage")
public class TestReduceStorageAction {
@Autowired
OrdersService ordersService;
@RequestMapping(value = "index/{ordersId}",method = RequestMethod.GET)
public ResultEntity index(@PathVariable int ordersId) throws ShopException {
ResultEntity resultEntity = new ResultEntity();
ordersService.testReduceStorage(ordersId);
resultEntity.setCode(ResultEntity.SUCCESS);
resultEntity.setMessage("扣减库存成功");
HashMap<String,Integer> hashMap = new HashMap<>();
hashMap.put("ordersId",ordersId);
resultEntity.setData(hashMap);
return resultEntity;
}
}
//测试高并发情况下,扣减库存是否覆盖修改库存
public void testReduceStorage(int ordersId) throws ShopException {
//根据订单Id到订单实体表中把商品查询出来,主要是为了减少库存
List<OrdersGoods> ordersGoodsListByOrdersId = ordersGoodsDao.getOrdersGoodsListByOrdersId(ordersId);
List<OrdersGoods> seckillGoods = goodsService.isSeckillGoods1(ordersGoodsListByOrdersId);
if (seckillGoods.size() > 0) {
//直接操作减库存即可,不用验证
buyOrdersService.updateGoodsStorageSales(seckillGoods);
}
}
2、使用jmeter高并发扣减库存
一个商品初始化库存5000,并发执行100个线程同时扣减库存,循环40次,预期累计扣减库存4000,经过测试库存扣减正常,并没有多扣或少扣,商品最后库存1000,说明扣减库存的悲观锁是正常运行的。
3、高并发执行扣减库存操作时,同时调取修改库存接口,验证修改库存操作是否被扣减库存操作覆盖。
验证原理:
一个商品初始化库存5000,并发执行100个线程同时扣减库存,循环10次,预期累计扣减库存1000,高并发执行扣减库存操作期间,发起一次修改库存为5000的操作,如果最后库存大于4000,说明修改库存的操作没有被覆盖,如果最后库存等于4000说明修改库存的操作被覆盖。
验证结果:
经多次测试,商品库存都大于4000,修改库存操作并没有被扣减库存操作覆盖的情况。
原因分析:
扣减库存一旦获得了悲观锁,修改库存就不可能先于扣减库存修改数据,所以修改库存操作不会被扣减库存操作覆盖。
4、将服务部署在两个tomcat里面,验证高并发扣减库存锁是否有效。
将web服务部署在两个tomcat,同时发起多线程扣减库存的操作,经多次测试库存没有多扣或者少扣,扣减库存的过程中,发起修改库存的操作,修改库存的操作没有被覆盖。
原因分析:
扣减库存是对数据库加的行锁,两个服务共用一个数据库,数据库加锁是有效的。