从零学习redis08,分布式锁

一:

利用SetEX+设置过期时间进行分布式锁的实践:

 public Result order(Long voucherId) {
        SeckillVoucher seckillVoucher = iSeckillVoucherService.getById(voucherId);
        LocalDateTime beginTime = seckillVoucher.getBeginTime();
        LocalDateTime endTime = seckillVoucher.getEndTime();
        //现在时间在秒杀活动之前
        if (beginTime.isAfter(LocalDateTime.now())) {
            return Result.fail("秒杀活动还没开始!");
        }
        //现在时间在秒杀活动之后
        if (endTime.isBefore(LocalDateTime.now())) {
            return Result.fail("秒杀活动已经结束!");
        }
        //判断库存是否充足
        if (seckillVoucher.getStock()<1){
            return Result.fail("库存不足");
        }

        Long userId = UserHolder.getUser().getId();
        System.out.println("====>"+userId);
        //TODO 先释放锁,然后再提交事务
        //和 spring代理对象不同

         Lock voucherOrder = new Lock("voucherOrder", stringRedisTemplate);
         boolean isLock = voucherOrder.tryLock(1200);
         //判断是否获取锁成功
        if (!isLock) {
            //重试或返回错误信息
            return Result.fail("不允许重复下单");
        }
        try {
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.getResult(voucherId);
        } catch (IllegalStateException e) {
            throw new RuntimeException(e);
        }finally {
            voucherOrder.unlock();
        }

    }


    @Transactional(rollbackFor = Exception.class)
    public  Result getResult(Long voucherId) {
        Long userId = UserHolder.getUser().getId();
        Integer count = query().eq("user_id", UserHolder.getUser().getId()).eq("voucher_id", voucherId).count();
        System.out.println(count);
        //判断用户是否下过单
        if (count > 0) {
            return Result.fail("您已经下过订单");
        }
        //TODO 库存减1
        // 在执行减库存操作时,判断是否和查询时的库存一致,一致才可修改

            boolean success = iSeckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId).
                    gt("stock", 0).update();
            if (!success) {
                return Result.fail("库存不足,秒杀失败!");
            }
            //TODO 创建订单
            VoucherOrder order = new VoucherOrder();
            long orderID = idWorker.nextId(RedisConstants.SECKILL_STOCK_KEY);
            order.setVoucherId(voucherId);
            order.setId(orderID);
            order.setUserId(userId);
            save(order);

            //TODO 返回订单ID
            return Result.ok(orderID);
        }

当检查完之后如果此时业务发生阻塞。线程二开始执行,线程一唤醒后继续删除,仍然会删除线程二的锁。所以要保证查询与删除的原子性,所以我们可以使用lua脚本

实现代码:

local id =redis.call('get',KEYS[1])
if(id==ARGV[1]) then
    --释放锁
    redis.call('del',KEYS[1])
end
return 0
   @Override
   public void unlock() {
    //获取线程标识
     stringRedisTemplate.execute(UN_LOCK_SCRIPT, Collections.singletonList(KEY_PREFIX+name),
             ID_PREFIX+Thread.currentThread().getId());


   }
   }

 使用redisson

引入依赖

           <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.17.5</version>
        </dependency>

配置redisson

@Configuration
public class RedisConfig {
    @Bean
    public RedissonClient redissonClient(){
         Config config = new Config();
         //使用单点地址。集群地址使用config.useClusterServers()
         config.useSingleServer().setAddress("redis://119.29.57.85:6379").setPassword("redis777");
         return Redisson.create(config);

    }
}
@Autowrite
private RedissonClient redissionclient;

    public Result order(Long voucherId) {
        SeckillVoucher seckillVoucher = iSeckillVoucherService.getById(voucherId);
        LocalDateTime beginTime = seckillVoucher.getBeginTime();
        LocalDateTime endTime = seckillVoucher.getEndTime();
        //现在时间在秒杀活动之前
        if (beginTime.isAfter(LocalDateTime.now())) {
            return Result.fail("秒杀活动还没开始!");
        }
        //现在时间在秒杀活动之后
        if (endTime.isBefore(LocalDateTime.now())) {
            return Result.fail("秒杀活动已经结束!");
        }
        //判断库存是否充足
        if (seckillVoucher.getStock()<1){
            return Result.fail("库存不足");
        }

        Long userId = UserHolder.getUser().getId();
        System.out.println("====>"+userId);
        //TODO 先释放锁,然后再提交事务
        //和 spring代理对象不同

         //Lock voucherOrder = new Lock("voucherOrder", stringRedisTemplate);
         RLock lock = redissonClient.getLock("lock:order:" + userId);
         boolean isLock = lock.tryLock();
        //boolean isLock = voucherOrder.tryLock(1200);
         //判断是否获取锁成功
        if (!isLock) {
            //重试或返回错误信息
            return Result.fail("不允许重复下单");
        }
        try {
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.getResult(voucherId);
        } catch (IllegalStateException e) {
            throw new RuntimeException(e);
        }finally {
           lock.unlock();
        }

    }

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值