20240721
- 一、分布式锁
- 1. 什么是分布式锁
- 2. 分布式锁的实现
- 3. 基于redis的分布式锁
- 4 总结
- 二、对于lua脚本可以保证事务,要么成功要么失败。
- 1. 在redis中调用lua脚本
- 三、Redisson
- 1 步骤
- 2. Redisson的总结
- 3. 几种分布式锁的区别
- 三、优化我们的秒杀
- 1. 我们在创建优惠券的时候,就把他存入redis中,然后对于判断库存和id是否重复下单的操作,我们就在redis中操作
- 2. 编写lua脚本
- 3. 在代码里面运行lua脚本
(来源于黑马—太困了,有一些忘了,每天补)
一、分布式锁
1. 什么是分布式锁
因为我们会有多个线程,比如我们的后端服务器有多个,通过nginx进行负载均衡,就会导致我们锁失效,多个服务器都能访问到。
2. 分布式锁的实现
3. 基于redis的分布式锁
4 总结
二、对于lua脚本可以保证事务,要么成功要么失败。
1. 在redis中调用lua脚本
三、Redisson
1 步骤
也可以在配置文件中配置,但可能会导致和redis的配置冲突,所以写成配置类。
2. Redisson的总结
3. 几种分布式锁的区别
三、优化我们的秒杀
1. 我们在创建优惠券的时候,就把他存入redis中,然后对于判断库存和id是否重复下单的操作,我们就在redis中操作
@Override@Transactionalpublic 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 库存不足,返回1return 1 -- 什么意思
end
-- 3.3 判断用户是否已经购买过,SISMEMBER orderKye userId 对于set类型 sadd是添加,SISMEMBER是判断是否存在
if(tonumber(redis.call('SISMEMBER',orderKey,userId)) ==1) then--3.4 重复下单,返回2return 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*/@Overridepublic 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.判断结果是否为0int i = result.intValue();//转型if(result!=0){//2.1不为零,没有购买资格return Result.fail(result == 1 ? "库存不足" : "不能重复下单");}//2.2为0,有购买资格,把下单信息保存到阻塞队列long orderId=redisIdWorker.nextId("order");//用我们之前的生成全局唯一的订单id方法生成订单//TODO 保存到阻塞队列//3.返回订单idreturn Result.ok(orderId);}