• 【Redis】Redis实现分布式锁


    【Redis】Redis常见面试题(1)

    在这里插入图片描述

    【Redis】Redis常见面试题(1)

    1. 为什么要用分布式锁

    之前学到的锁,在分布式,微服务是不适用的,因为之前的锁针对的是本地线程,而分布式是跨机器的

    而Redis作为一个独立的三方系统,其天生的优势就是可以作为一个分布式系统来使用,因此使用Redis实现的锁都是分布式锁!

    在这里插入图片描述

    2. Redis如何实现分布式锁

    Redis实现分布式锁可以通过setnx(set if not exists)命令实现,当我们使用setnx创建键值成功则表明加锁成功,否则即加锁失败(需要等待锁释放,自旋/挂起等等…);del删除键值,表示锁释放

    • 也就是说,Redis实现锁机制没有那么的高深莫测和复杂,而是一个逻辑概念:
      • 一个锁🔐就是一个键值,争夺锁就是争夺对这个键值的设置权
      • 如果键值被设置了,则说明锁被占用
      • 如果键值不存在,则说明锁没被占用
    • 值是什么具体而论,对于争夺锁的机制而已,是存不存在比较重要
    • 而这个键值对也可以被看成普通的键值对,setnx和del不止用在分布式锁的实现,它们只是具有特殊含义的命令罢了
      • 又由于Redis天生支持分布式系统,所以这个键值可以被所有机器看到(满足了一个前提,所有机器可以setnx 和 del同一个键值),也就是这个锁🔐就是分布式锁

    获得锁🔐:

    127.0.0.1:6379> setnx lock1 true
    
    • 1

    在这里插入图片描述

    再次获得这把锁:

    在这里插入图片描述

    在这里插入图片描述

    就是个普通键值对~

    释放锁🔐

    127.0.0.1:6379> del lock1
    
    • 1

    在这里插入图片描述

    再次释放锁:

    在这里插入图片描述

    那么我们用两个命令行操作redis,模拟两个线程,讲解Redis分布式锁的一些相关问题

    在这里插入图片描述

    在这里插入图片描述

    3. Redis接受多个请求模拟演示

    两个命令行“同时”尝试获取锁,总有一个会获取到锁

    在这里插入图片描述

    黑色命令行选择自旋等待锁:

    在这里插入图片描述

    直到白色命令行释放锁:

    在这里插入图片描述

    黑色命令行才获取到锁:

    在这里插入图片描述

    4. 使用Redis实现分布式锁会存在什么问题

    4.1 一个锁被长时间占用

    1. 当一个应用突然奔溃、掉电、莫名其妙下线了,没来得及释放锁
    2. 可能会出现死锁,暂时不考虑可不可以重入锁的问题,N个线程M把锁的问题,也会导致死锁

    怎么处理:

    • 设置超时时间
    1. setnx 和 expire搭配使用,不太好,因为这样这条语句就是非原子性的,如果超时时间设置上之前奔溃了,依旧解决不了问题

    2. Redis 2.6.12 版本之后,提供了一个强大的功能,可以让这个操作是原子操作:

      • 在这里插入图片描述

      • 127.0.0.1:6379> set lock true ex 30 nx
        
        • 1
        • 可以参考后面的题词
        • ex time,单位为秒
        • px time,单位为毫秒
        • 不设置默认永不过期,但是不过期不代表不会被删除掉,这跟数据淘汰机制有关

    在这里插入图片描述

    4.2 锁误删

    由于这个键值对是公共可见的,所以一个线程是可以释放别的线程的锁的!从代码上我们不会出现刻意的删除别的线程的锁的恶劣行为,但是会有一些不可避免的偶然事件场景下,会出现这个问题:

    1. 白色线程抢到了锁🔒,黑色线程自旋

    在这里插入图片描述

    1. 但是白色线程执行花了35秒,锁已经自动释放了

    在这里插入图片描述

    1. 黑色线程抢到了锁,要执行10秒

    在这里插入图片描述
    在这里插入图片描述

    1. 白色线程不知道锁被释放并且被抢走了,在第35秒的时候,严谨规范地释放了锁!
      • 即使锁过期了,这也只是Redis的机制,原应用的业务依旧继续执行,(不要误解为应用在Redis中执行!多个应用只是利用的Redis的分布式锁的机制!这里的黑色线程和白色线程只不过是区分两个线程的redis操作)
      • 黑线程不知道自己的锁被释放了,并且锁可能也被其他更多线程获取了,黑线程也可能会误删别人的锁,混乱起来了,线程安全问题大大暴露!

    在这里插入图片描述

    这有点像,ABA的问题,参考此文章的aba模块:【JavaEE】多线程进阶问题-锁策略and死锁,CAS操作,Synchronized原理_s:103的博客-CSDN博客

    解决方法与之类似,(添加一个属性:一个标识或者版本号),保证释放锁是自己刚才抢到的锁!

    • 例如设置不一样的value

    白色线程设置white,黑色线程设置black

    也就是刚才的第四步,白色线程在释放锁之前,进行判断锁的归属:

    在这里插入图片描述

    ok,白色线程知道了,不能释放

    但是并不是完全没有问题,这个操作是两步的,非原子性操作!

    解决方案:

    1. 使用lua脚本,Redis可以识别的可保证原子性的脚本,写多长都是原子性的
    2. 项目使用Redisson框架,有支持原子性的分布式锁 -> 底层还是lua脚本,只不过你不用写,框架帮你写了
      • 一个复杂且好用的东西,总会有框架来方便使用😄

    感兴趣的同学可以自行学习:

    Redisson · GitHub


    文章到此结束!谢谢观看
    可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆


  • 相关阅读:
    QT的安装 [新版2022]
    堆排序(思路分析) [数据结构][Java]
    大数据学习之一——Hadoop单机部署
    《vue3实战》运用push()方法实现电影评价系统的添加功能
    Rabbitmq----分布式场景下的应用
    Windows Hook案例分析与技术探索
    【Java面试八股文宝典之基础篇】备战2023 查缺补漏 你越早准备 越早成功!!!——Day09
    三维模型3DTILE格式轻量化压缩主要技术方法浅析
    shell_57.Linux创建自己的重定向
    温故而知新:IIR滤波器设计的方法,幅频计算和参数理解
  • 原文地址:https://blog.csdn.net/Carefree_State/article/details/132853328