redis分布式锁实现

4 篇文章 0 订阅
3 篇文章 0 订阅

redis分布式锁

1 初识分布式锁

1.1 应用场景

  1. 防止缓存穿透
  2. 防止秒杀超卖
  3. 双写一致性
  4. 接口幂等性

1.2 多线程并发案例说明

1.2.1 案例

秒杀环境下:订单服务从库存中心拿到库存数,如果库存总数大于0,则进行库存扣减,并创建订单订单服务负责创建订单库存服务负责扣减库存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存失败,源站可能有防盗链机制,建议将图片保存下来直接上传上传(im-2VhXDV2SB-1659072511507)(C:\Users\PC\AppData\Roaming\Typora\typora-user-images\image-20220728222422315.png)(C:\Users\PC\AppData\Roaming\Typora\typora-user-images\image-20220728222422315.png)]

java代码如下,以下代码可能会出现超卖或者重复卖的问题:

运行结果如下:

在这里插入图片描述

1.2.2 解决方案
1.2.2.1 单体锁引入

引入synchornized 或 ReentrantLock ,单体锁可以保证原子性;

上图这种情况,在单程序中是ok的,但是在分布式情况下会出现如下场景:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hg75ryby-1659072511508)(C:\Users\PC\AppData\Roaming\Typora\typora-user-images\image-20220728223621408.png)]
在这里插入图片描述

分布式场景下,相当于一份业务代码copy了许多份,所以仅靠单体锁是无法解决以上问题的!

1.2.2.2 分布式锁

核心逻辑:

俗话说得好,没有什么问题是加一层解决不了的!

如果在各个服务之间,加一层公共资源,然后同时抢公共资源的锁,抢到的线程才能去执行对应业务逻辑,不就ok了吗?

在这里插入图片描述

抢到锁去执行,失败则自旋

在这里插入图片描述

当B执行完毕,将锁归还,则其他线程方可继续


抢资源的话,其实可以使用 setnx 命令来执行,因为只有当不存在时,才能写入这个key,其他线程如果晚一步,发现已经key已经被写入了,就知道自己不是第一个来的线程,所以就可以识趣的去自旋了;

对应java代码如下:

在这里插入图片描述

此时的运行结果就正常咯

在这里插入图片描述

2 分布式锁实现的问题

2.1 死锁 & 解决

如果某个线程在执行锁逻辑过程中宕机,导致没有删除锁

解决

  1. 添加过期时间;
  2. 原子性添加过期时间;

代码如下:

在这里插入图片描述

注!以上两种代码写法,一定要用第一种,防止线程恰好在他们之间死掉

2.2 锁不住

如果某个线程在执行过程中卡顿,导致卡过了超时时间

这问题咱们解决不了,一会儿有请 redisson 来解决

2.3 删除别人锁问题

该问题其实是锁不住问题的延续问题:当有线程A进入后由于超时,有其他线程B进入,此时redis中的锁是线程B的,而原来的线程A接着执行,线程A删除了别人的锁

解决:

  1. 给当前线程绑定一个局部变量uuid,由于每个线程都有一份属于自己的局部变量,那么线程和局部变量绑定之后,我们在删除锁之前判断一下,当前这把锁是不是自己的,再进行删除;

在这里插入图片描述

  1. 使用lua表达式解决;

    为什么有方法1了还要方法2呢?举个例子,当前线程恰好在这一步死了,(红线标注的位置)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ys8tHuXv-1659072511510)(C:\Users\PC\AppData\Roaming\Typora\typora-user-images\image-20220728231824462.png)]
    那就会又有对应问题!

    在这里插入图片描述

    解决: 将注释掉的代码替换为:

    代码的含义:

    redis.call(‘get’, KEYS[1]) ---- 调用’lock’这个key对应的值

    ARGV[1] ---- threadUuid这个值

    如果以上两个值相同,则调用以下方法:

    redis.call(‘del’, keys[1]) — 删除’lock’这个key

    否则返回;

    lua表达式是将被注释的这4行代码一样的逻辑,封装起来交给redis去做,可以保证原子性!就能解决“删除别人锁”的问题。

3 Redisson

3.1 redisson概念

Redisson是Redis服务器上的分布式可伸缩Java数据结构----驻内存数据网格(In-Memory Data Grid,IMDG)。

底层使用netty框架,并提供了与java对象相对应的分布式对象、分布式集合、分布式锁和同步器、分布式服务等一系列的Redisson的分布式对象。

点击转跳github源码

3.2 如何整合redisson

3.2.1 导入依赖

在这里插入图片描述

3.2.2 配置类

在这里插入图片描述

3.2.3 自动注入

在这里插入图片描述

3.2.4 代码测试

测试可重入锁
在这里插入图片描述


测试锁现象:

  1. redission是否给我们设置过期时间?
  2. redission能否续期?
  3. redission是否会死锁?

在这里插入图片描述

通过redis desktop manager可视化软件可以得到以下答案:

  1. 帮我们设置了过期时间,默认30s;
  2. 会给我们续期,会在剩余20s的时候续成30s,如果当前锁释放了,续期动作不会继续执行;
  3. 不会因为宕机导致死锁!

测试带时间的lock方法

redisson解决“删除别人锁”这个问题,采取的方法是抛出一个异常

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值