卖超问题经常出现在高并发系统中,现在来一个demo吧
首先建张表,stock
id bigint
product_id bigint
count int
version int
接着创建一个没有乐观锁的扣减库存的问题,我们会用Jmeter来模拟多线程请求接口来展现超卖问题,接着看
Controller,重点注意iStockService.updateStock(productId,null);
updateStock第二个参数为,version,版本控制,这也是乐观锁的实现方式
@PostMapping
public String reduceStock(@RequestParam Long productId){
Stock one = iStockService.getOne(new QueryWrapper<Stock>().lambda().eq(Stock::getProductId, productId));
log.info("stock_num:"+one.getCount());
if (one.getCount() > 0){
boolean successOrNot = iStockService.updateStock(productId,null);
return successOrNot ? "good" : "fail";
}
return null;
}
service
@Service
public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements IStockService {
@Autowired
private StockMapper stockMapper;
@Override
public boolean updateStock(Long productId, Integer version) {
return stockMapper.updateStock(productId,version);
}
}
mapper
public interface StockMapper extends BaseMapper<Stock> {
boolean updateStock(@Param("productId") Long productId,@Param("version") Integer version);
}
xml
>
UPDATE stock SET count = count - 1,version = version + 1
>
!= null and productId !=''">
and product_id = #{productId}
>
!= null">
and version = #{version}
>
>
>
好了所有的代码,接着先来看看jmeter的使用,然后开始测试
在正式贴超卖代码前,先来看看jmeter的基本使用
用来测试的是5.4.3版本,各位可以参考
下载地址
在jmeter的bin目录下找到jemeter.properties,打开jmeter.properties,找到language选项,设定为zh_CN
language=zh_CN
打开就可以看到是中文的了
吐槽一下,CSDN的图片都不能上传,是真的坑,展示只能用中文?什么鬼…考虑换个博客了,神坑
然后测试计划右键添加–>线程(用户)–>线程组
在线程数那设置100左右的线程,循环次数1,Ramp-Up 1
线程组那,右键添加–>取样器–>HTTP请求
在Http请求基本那做Web服务器的,协议、IP、端口号、路径、参数等地方做相应设置
如有参数,在参数底部选择添加填入参数名、参数值即可启动
在线程组处,添加–>监听器–>观察结果树,好的所有配置已完成,让我们开始测试
2022-06-12 12:54:55.493 INFO 7380 --- [io-8021-exec-48] ho.controller.StockController : stock_num:-34
2022-06-12 12:54:55.493 INFO 7380 --- [io-8021-exec-49] ho.controller.StockController : stock_num:-36
2022-06-12 12:54:55.493 INFO 7380 --- [io-8021-exec-29] ho.controller.StockController : stock_num:-36
接着可以看到,库存数量为负的了,也就是卖超了,所以怎么办呐?
来看看解决方案把,
给version一个默认值(数据库中设置),并且将version传到mapper中,如下
iStockService.updateStock(productId,one.getVersion());
@PostMapping
public String reduceStock(@RequestParam Long productId){
Stock one = iStockService.getOne(new QueryWrapper<Stock>().lambda().eq(Stock::getProductId, productId));
log.info("stock_num:"+one.getCount());
if (one.getCount() > 0){
boolean successOrNot = iStockService.updateStock(productId,one.getVersion());
return successOrNot ? "good" : "fail";
}
return null;
}
可以再看看,从输出中可以看到,没有问题了,这就是用乐观锁,卖超问题的解决方案
2022-06-12 13:09:03.827 INFO 2460 --- [io-8021-exec-19] ho.controller.StockController : stock_num:10
2022-06-12 13:09:03.852 INFO 2460 --- [io-8021-exec-10] ho.controller.StockController : stock_num:9
2022-06-12 13:09:03.868 INFO 2460 --- [nio-8021-exec-1] ho.controller.StockController : stock_num:8
2022-06-12 13:09:03.905 INFO 2460 --- [io-8021-exec-31] ho.controller.StockController : stock_num:7
2022-06-12 13:09:03.913 INFO 2460 --- [nio-8021-exec-9] ho.controller.StockController : stock_num:6
2022-06-12 13:09:03.929 INFO 2460 --- [nio-8021-exec-2] ho.controller.StockController : stock_num:5
2022-06-12 13:09:03.945 INFO 2460 --- [nio-8021-exec-6] ho.controller.StockController : stock_num:4
2022-06-12 13:09:03.977 INFO 2460 --- [io-8021-exec-14] ho.controller.StockController : stock_num:3
2022-06-12 13:09:03.991 INFO 2460 --- [io-8021-exec-28] ho.controller.StockController : stock_num:2
2022-06-12 13:09:04.006 INFO 2460 --- [io-8021-exec-15] ho.controller.StockController : stock_num:1
2022-06-12 13:09:04.037 INFO 2460 --- [io-8021-exec-30] ho.controller.StockController : stock_num:0
2022-06-12 13:09:04.061 INFO 2460 --- [io-8021-exec-23] ho.controller.StockController : stock_num:0