redis分布式锁解决高并发(预下单)

2021-08-30  本文已影响0人  Raral

分布式锁应用(预下单)

  1. controller
   //添加分布式锁
    @PostMapping("create_order2")
    public AjaxResult createOrder2(@RequestBody CreateOrderReq req) {
        String lockKey = req.getId();//锁(业务id)
        String threadId = IDGeneratorUtils.getUUID().toString();//线程唯一标识
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey,threadId, 10, TimeUnit.SECONDS);

        if(!flag) {
            return  AjaxResult.error("当前系统繁忙,请稍后再试");
        }
        AjaxResult order = null;
        try {
            order = skuOrderService.createOrder(req);
        } catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        } finally {
            //在释放锁时候,保证是当前线程id加的锁
            if(threadId.equals(redisTemplate.opsForValue().get(lockKey))) {
                //假如这个卡顿一下,可能会删除 下一个线程2加的锁
                //释放当前业务完成后释放锁
                redisTemplate.delete(lockKey);
            }
        }
        return order;
    }
  1. service
 @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult createOrder(CreateOrderReq req) throws CustomException {
        //校验库存是否为0
        Sku sku = skuMapper.selectOne(Wrappers.<Sku>lambdaQuery().eq(StringUtils.isBlank(req.getId()), Sku::getId, req.getId()));
        if(!Objects.isNull(sku)) {
//            shopPoolWater.getBusinessNum() > shopPool.getStock() - shopPool.getFrozenNum()
            if(req.getTradeNum() > sku.getStock() - sku.getFrozenNum()) {
                return AjaxResult.error("库存不足");
            }
        }else {
            return AjaxResult.error("查询当前商品为null");
        }


        //冻结库存
        skuMapper.addFrozenNum(req.getId(),req.getTradeNum());
        AtomicInteger atomicInteger = new AtomicInteger();
        int andIncrement = atomicInteger.getAndIncrement();
        System.out.println(atomicInteger.get());

        //创建订单
        SkuOrder skuOrder = SkuOrder.builder()
                .id(IdWorker.getId()).skuId(req.getId()).orderCode(OrderUtil.generateOrderNo("cs")).status("4").tradeNum(req.getTradeNum()).uid(req.getUid())
                .build();

        SkuOrderServiceImpl skuOrderService =(SkuOrderServiceImpl) AopContext.currentProxy();
        boolean save = false;
        try {
            save = skuOrderService.save(skuOrder);
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException(e.getMessage(), 500);
        }
        if(save){
            //减少冻结数量,减少库存,增加领取数量
            skuMapper.updateStock(req.getId(), req.getTradeNum());
            Sku sku2 = skuMapper.selectOne(Wrappers.<Sku>lambdaQuery().eq(StringUtils.isBlank(req.getId()), Sku::getId, req.getId()));
            Integer stock = sku2.getStock();

            return AjaxResult.success("下单和更新库存成功,剩余库存:" + stock);
        }else {
            return AjaxResult.error("下单和更新库存失败");
        }

    }
  1. mapper maybatis自带的
public interface SkuMapper extends BaseMapper<Sku> {

    /**
     * 增加冻结数量
     * */
    @Update({"update test_product set frozen_num = frozen_num + #{tradeNum} where id = #{id}"})
    void addFrozenNum(@Param("id") String id, @Param("tradeNum") Integer tradeNum);
    /**
     * 更新库存
     * */
    @Update({"update test_product set stock = stock - #{tradeNum}, receive_num = receive_num + #{tradeNum}, frozen_num = frozen_num - #{tradeNum} where stock >= #{tradeNum} and frozen_num >= #{tradeNum} and id = #{id}"})
    void updateStock(@Param("id") String id, @Param("tradeNum") Integer tradeNum);
}

  1. domain
@Data
@ToString
@EqualsAndHashCode(callSuper = false)
@TableName("test_product_order")
@Builder(toBuilder = true)
public class SkuOrder implements Serializable {
    private static final long serialVersionUID=1L;

    @TableId(value = "id", type= IdType.INPUT)
    private Long id;
    @TableField(value = "sku_id")
    private String skuId;
    @TableField(value = "order_code")
    private String orderCode;
    @TableField(value = "trade_num")
    private Integer tradeNum;
    @TableField(value = "status")
    private String status;
    @TableField(value = "uid")
    private String uid;
}

```@Data
@ToString
@EqualsAndHashCode(callSuper = false)
@TableName("test_product")
public class Sku implements Serializable {
    private static final long serialVersionUID=1L;

    @TableId(value = "id", type= IdType.INPUT)
    private Long id;
    @TableField(value = "sku_name")
    private String skuName;
    @TableField(value = "stock")
    private Integer stock;
    @TableField(value = "init_num")
    private Integer initNum;
    @TableField(value = "receive_num")
    private Integer receiveNum;
    @TableField(value = "frozen_num")
    private Integer frozenNum;

}
  1. 使用jmeter 测试 高并发 2000个 结果
    微信图片_20210830103154.png
    微信图片_20210830103203.png
    bug
  2. 通过图上,当1秒内2000次并发,库存没有变变成负数,但是下单数量多了19条记录

咋样解决?

敬请期待

上一篇下一篇

猜你喜欢

热点阅读