• redis分布式锁实现


    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解决“删除别人锁”这个问题,采取的方法是抛出一个异常

  • 相关阅读:
    传统企业如何转型社交电商,泰山众筹的玩法有哪些?
    javaEE进阶 - Spring 更简单的读取和存储对象 - 细节狂魔
    Matlab:在键入时检查语法
    Ajax--form表单与模板引擎--模板引擎的基本概念 - art-template模板引擎
    计算机网络第七章知识点回顾(自顶向下)
    logback-spring.xml配置文件标签(超详解)
    数据库连接池的使用
    【Spring入门学习】
    山西电力市场日前价格预测【2023-09-17】
    实战三十一:基于LightGCN推荐算法的推荐系统详细教程代码+数据
  • 原文地址:https://blog.csdn.net/weixin_40597409/article/details/126053859