美文网首页
redis分布式锁解决高并发(预下单)

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

作者: Raral | 来源:发表于2021-08-30 10:39 被阅读0次

    分布式锁应用(预下单)

    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条记录

    咋样解决?

    敬请期待

    相关文章

      网友评论

          本文标题:redis分布式锁解决高并发(预下单)

          本文链接:https://www.haomeiwen.com/subject/pdiniltx.html