使用redis简单实现分布式锁

本文探讨了如何使用原生Redis和Redisson库来保证优惠券发放过程的原子性,包括校验、库存检查、扣减与记录,同时展示了两种技术在分布式锁应用上的对比.

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注: 只是单纯的记录代码: 来源小D课堂

  1. 使用原生的方式实现,要点在于保证原子性操作
    /**
     * 1. 校验该优惠卷是否存在
     * 2. 检验优惠卷是否可以领取: 时间、库存、超过限制
     * 3. 扣减库存
     * 4. 保存领卷记录
     *
     * @param couponId
     * @param promotion
     * @return
     */
    @Override
    public ApiResult addCoupon(long couponId, CategoryCouponEnums promotion) {
        //获取当前用户信息
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        String uuid = CommonUtil.generateUUID();
        String key = "lock:coupon:"+couponId;
        //加锁设置过期时间
        boolean lockResult = redisTemplate.opsForValue().setIfAbsent(key,uuid, Duration.ofSeconds(30));
        if (lockResult){
            log.info("加锁成功:{}",couponId);
            try{
                //执行正常逻辑
                //判断优惠卷是否存在
                CouponDO couponDO = this.getOne(new QueryWrapper<CouponDO>()
                        .eq("id",couponId)
                        .eq("category",promotion.name())
                        .eq("publish",CouponPublishEnums.PUBLISH.name()));
                if (couponDO==null){
                    throw new BizException(BaseCodeEnums.COUPON_NOT_EXITS);
                }
                //检查优惠卷是否可用
                this.checkCoupon(couponDO,loginUser);
                //构建领卷记录
                CouponRecordDO couponRecordDO = new CouponRecordDO();
                BeanUtils.copyProperties(couponDO,couponRecordDO);
                couponRecordDO.setCreateTime(new Date());
                couponRecordDO.setUseState(CouponUserStateEnums.NEW.name());
                couponRecordDO.setUserId(loginUser.getId());
                couponRecordDO.setUserName(loginUser.getName());
                couponRecordDO.setId(null);
                couponRecordDO.setCouponId(couponId);

                //扣减库存
                int stock = couponRecordService.reduceStock(couponId);
                if (stock==1){
                    couponRecordService.save(couponRecordDO);
                }else {
                    log.warn("发放优惠卷失败:{},用户:{}",couponDO,loginUser);
                    throw new BizException(BaseCodeEnums.COUPON_NO_STOCK);
                }
            }finally {
                //使用lua脚本来保证删除锁的原子性操作
                String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
                Integer execute = redisTemplate.execute(new DefaultRedisScript<>(script, Integer.class), Arrays.asList(key), uuid);
                log.info("解除锁:{}",execute);
            }
        }else{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                log.error("自旋失败");
                e.printStackTrace();
            }
            addCoupon(couponId,promotion);
        }
        return ApiResult.buildSuccess();
    }
  1. 使用redisson实现
  • 引入jar包
        <!--redisson分布式锁-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
        </dependency>
  • 代码
    /**
     * 1. 校验该优惠卷是否存在
     * 2. 检验优惠卷是否可以领取: 时间、库存、超过限制
     * 3. 扣减库存
     * 4. 保存领卷记录
     *
     * @param couponId
     * @param promotion
     * @return
     */
    @Override
    public ApiResult addCoupon(long couponId, CategoryCouponEnums promotion) {
        //获取当前用户信息
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        String key = "lock:coupon:"+couponId;
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        log.info("领卷接口加锁成功:{}",couponId);
        try{
            //执行正常逻辑
            //判断优惠卷是否存在
            CouponDO couponDO = this.getOne(new QueryWrapper<CouponDO>()
                    .eq("id",couponId)
                    .eq("category",promotion.name())
                    .eq("publish",CouponPublishEnums.PUBLISH.name()));
            if (couponDO==null){
                throw new BizException(BaseCodeEnums.COUPON_NOT_EXITS);
            }
            //检查优惠卷是否可用
            this.checkCoupon(couponDO,loginUser);
            //构建领卷记录
            CouponRecordDO couponRecordDO = new CouponRecordDO();
            BeanUtils.copyProperties(couponDO,couponRecordDO);
            couponRecordDO.setCreateTime(new Date());
            couponRecordDO.setUseState(CouponUserStateEnums.NEW.name());
            couponRecordDO.setUserId(loginUser.getId());
            couponRecordDO.setUserName(loginUser.getName());
            couponRecordDO.setId(null);
            couponRecordDO.setCouponId(couponId);

            //扣减库存
            int stock = couponRecordService.reduceStock(couponId);
            if (stock==1){
                couponRecordService.save(couponRecordDO);
            }else {
                log.warn("发放优惠卷失败:{},用户:{}",couponDO,loginUser);
                throw new BizException(BaseCodeEnums.COUPON_NO_STOCK);
            }
        }finally {
            lock.unlock();
            log.info("解除锁:{}",couponId);
        }
        return ApiResult.buildSuccess();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

synda@hzy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值