分布式锁、Lua脚本、redisson、运行lua脚本优化代码


(来源于黑马—太困了,有一些忘了,每天补)

一、分布式锁

1. 什么是分布式锁

因为我们会有多个线程,比如我们的后端服务器有多个,通过nginx进行负载均衡,就会导致我们锁失效,多个服务器都能访问到。
在这里插入图片描述

2. 分布式锁的实现

在这里插入图片描述

3. 基于redis的分布式锁

在这里插入图片描述

4 总结

在这里插入图片描述

二、对于lua脚本可以保证事务,要么成功要么失败。

1. 在redis中调用lua脚本

在这里插入图片描述

三、Redisson

1 步骤

也可以在配置文件中配置,但可能会导致和redis的配置冲突,所以写成配置类。
在这里插入图片描述
在这里插入图片描述

2. Redisson的总结

在这里插入图片描述

3. 几种分布式锁的区别

在这里插入图片描述

三、优化我们的秒杀

在这里插入图片描述

1. 我们在创建优惠券的时候,就把他存入redis中,然后对于判断库存和id是否重复下单的操作,我们就在redis中操作

    @Override
    @Transactional
    public void addSeckillVoucher(Voucher voucher) {
        // 保存优惠券
        save(voucher);
        // 保存秒杀信息
        SeckillVoucher seckillVoucher = new SeckillVoucher();
        seckillVoucher.setVoucherId(voucher.getId());
        seckillVoucher.setStock(voucher.getStock());
        seckillVoucher.setBeginTime(voucher.getBeginTime());
        seckillVoucher.setEndTime(voucher.getEndTime());
        seckillVoucherService.save(seckillVoucher);
        //保存秒杀优惠券的库存到Redis中
        stringRedisTemplate.opsForValue().set("secKill:stock:" + voucher.getId(), voucher.getStock().toString());
    }

2. 编写lua脚本

-- 1.参数列表
-- 1.1 优惠券id
local voucherId= ARGV[1]
-- 1.2 用户id
local userId=ARGV[2]

-- 2.数据key
-- 2.1 库存key
local stockKey='secKill:stock:'..voucherId -- 连接的时候,是使用..来连接  里面存的是库存
-- 2.2 订单key
local orderKey='secKill:order'..voucherId   --  里面存的是下单人的id


-- 3.脚本业务
-- 3.1 判断库存是否充足,get stockKey
if(tonumber(redis.call('get',stockKey)) <=0) then  -- redis.call('get',stockKey)返回的是一个string类型的,无法和0进行比较,所以,转换
    -- 3.2 库存不足,返回1

return 1 -- 什么意思
end
-- 3.3 判断用户是否已经购买过,SISMEMBER orderKye userId   对于set类型 sadd是添加,SISMEMBER是判断是否存在
if(tonumber(redis.call('SISMEMBER',orderKey,userId)) ==1) then
    --3.4 重复下单,返回2
    return 2
end
-- 3.5 扣减库存,incrby stockKey -1
redis.call('incrby',stockKey,-1)
-- 3.6 下单,sadd orderKye userId
redis.call('sadd',orderKey,userId)
return 0


3. 在代码里面运行lua脚本

 /**
     * * 使用lua脚本实现秒杀
     */
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;//DefaultRedisScript 是 Spring Data Redis 提供的一个类,用于封装 Redis Lua 脚本。在这个上下文中
    static {
//static { ... }
//这是一个静态初始化块,用于在类加载时初始化 SECKILL_SCRIPT 实例。
//在这个块中,设置了 SECKILL_SCRIPT 的具体属性,包括脚本的位置和结果类型。
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }
    /**
     * 使用lua
     * @param voucherId
     * @return
     */
    @Override
    public Result seckillVoucher(Long voucherId) {
        //获取用户
        Long userId= UserHolder.getUser().getId();
        //1.执行lua脚本
        Long result = stringRedisTemplate.execute(//总共三个参数,第一个参数是我们的lua脚本,第二个是KEY的参数,第三个是ARGV的参数(有两个)
                SECKILL_SCRIPT,
                Collections.emptyList(),//KEY的参数,传一个空集合
                voucherId.toString(),//ARGV的参数
                userId.toString()
        );
        //2.判断结果是否为0
        int i = result.intValue();//转型
        if(result!=0){
            //2.1不为零,没有购买资格
            return Result.fail(result == 1 ? "库存不足" : "不能重复下单");
        }
        //2.2为0,有购买资格,把下单信息保存到阻塞队列
        long orderId=redisIdWorker.nextId("order");//用我们之前的生成全局唯一的订单id方法生成订单
        //TODO 保存到阻塞队列

        //3.返回订单id
        return Result.ok(orderId);
    }
  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值