商城里创建订单对库存的脚校验,怎么防止多卖超卖?
这里我分别使用了synchronized,rabbitmq,和数据库判断,数据库锁,
首先说rabbitMq吧!先看代码首先我声明了一个名为myQueue3的队列
//声明队列
@Bean
public Queue queue1(){
return new Queue("myQueue3");
}
建立消费者和生产者
getcort1 模拟了创建订单操作 为消息的提供者
@RequestMapping(value = "/getcort1")
public String send() {
String context = "order_ok " + new Date();
System.out.println("Sender : " + context);
// 调用 发送消息的方法
//convertAndSend 发送消息返回为空
//convertSendAndReceive发送消息 可接收回复
Object a = rabbitTemplate.convertSendAndReceive("myQueue3", context);
System.out.println("收到"+a);
return "创建订单成功";
}
test为消费者,并且为生产者反馈了扣减库存的结果
@RabbitListener(queues ="myQueue3")
@RabbitHandler
@ApiOperation(value = "压力测试", notes = "压力测试")
@RequestMapping(value = "/test", method = RequestMethod.POST)
@Transactional(rollbackFor = {RuntimeException.class, Error.class})
public String test(String message) throws Exception {
System.out.println("收到了消息:"+message);
//查询库存
HotelProduct hotelProduct = hotelProductMapper.selectByPrimaryKey(1);
//库存充足扣减,不足返回
if (hotelProduct.getQuantity()>0){
hotelProduct.setQuantity(hotelProduct.getQuantity() - 1);
System.out.println("剩余库存:"+hotelProductMapper.selectByPrimaryKey(1).getQuantity());
hotelProductMapper.updateByPrimaryKeySelective(hotelProduct);
return "ok";
} else {
System.out.println("库存不足");
return "no";
}
}
测试一下结果
可以看到1000个请求全部成功,
是没有出现超卖的情况的
消息队列扣减库存比平时还是有一个好处的,我们将数据库库存删掉,制造一个空指针
虽然扣减库存报错但是不影响订单的创建[图片上传中...(捕获.PNG-bcae0a-1610510811626-0)]
将库存补上以后,扣减库存
synchronized关键字
@ApiOperation(value = "压力测试", notes = "压力测试")
@RequestMapping(value = "/test", method = RequestMethod.POST)
@Transactional(rollbackFor = {RuntimeException.class, Error.class})
public synchronized String test() throws Exception {
System.out.println("创建订单");
HotelProduct hotelProduct = hotelProductMapper.selectByPrimaryKey(1);
if (hotelProduct.getQuantity()>0){
hotelProduct.setQuantity(hotelProduct.getQuantity() - 1);
System.out.println("剩余库存:"+(hotelProductMapper.selectByPrimaryKey(1).getQuantity()));
hotelProductMapper.updateByPrimaryKeySelective(hotelProduct);
return "ok";
} else {
System.out.println("库存不足");
return "no";
}
}
相同条件测试一下
数据库逻辑操作 sql
<update id="quantity">
UPDATE hotel_product
SET quantity = quantity - 1
WHERE
id = 1
AND quantity - 1 >= 0
</update>
@ApiOperation(value = "压力测试", notes = "压力测试")
@RequestMapping(value = "/test", method = RequestMethod.POST)
@Transactional(rollbackFor = {RuntimeException.class, Error.class})
public String test() throws Exception {
System.out.println("创建订单");
if (productService.quantity() > 0) {
System.out.println("成功,剩余库存:" + hotelProductMapper.selectByPrimaryKey(1).getQuantity());
return "ok";
} else {
System.out.println("库存不足");
return "no";
}
}
数据库悲观锁
select * from hotel_product where id=1 for update;
补充一下,面试的时候受到前辈指点,后面去了解一下,大家都知道redis本来就是单线程的,所以我们可以考虑把“检查库存-扣减库存-返回结果”的动作写成lua脚本去调用,一个调用就能原子化地操作库存了