Redisson的分布式锁

234 阅读3分钟

一、为什么要用Redisson的分布式锁?

  1. JAVA自带的Synchronized\Lock等锁,都是JVM控制的锁,属于单机锁。可用于单机并发的场景,但是在分布式高并发场景下,单机锁无法锁住资源。
  2. 使用Redis的setnx命令的分布式锁,有以下缺点:
    • 是不可重入的,同一个线程无法多次获取同一把锁。
    • 不可重试,获取锁只尝试一次就返回false,没有重试机制。
    • 锁超时释放,虽然可以避免死锁,但如果是业务执行耗时较长,也会导致锁释放,存在安全隐患。

二、Redisson分布式锁的执行流程

1688563777829.jpg

1693020872879.jpg

  1. 线程A尝试加锁,加锁成功后,会开启一个新线程充当看门狗,默认每10s执行一次续期操作。默认的过期时间是30s。
  2. 此时线程B也来尝试加锁,加锁失败后,会一直while循环尝试获取锁。到达阈值waitTime后不再尝试加锁。这就是分布式锁的重试机制。

加入重试机制的好处是:在高并发情况下,可以最大限度的提高锁的使用频率。

三、Redisson分布式锁的原理

3.1 加锁

1693016502395.png

3.1.1 Redisson锁是可重入锁

  1. 可重入:一个线程在持有锁的请求下,可以再次请求锁而不被阻塞。

  2. 为什么Redisson分布式锁是可重入的?

    因为锁是利用hash结构记录线程ID和重入次数的

keyvaluevlaue
filedvalue
locknamethreadID锁重入次数

3.1.2 Redisson锁是可以失败重试的

3.1.3 Redisson锁的自动续期--看门狗原理

3.2 释放锁

1693016688125.jpg

三、分布式锁的特性--主从一致性

1. 为什么会发生主从数据不一致?

Qr914BzUjz.jpg

  1. 线程1 在redis master节点获取锁lock成功。
  2. 在master节点向slave节点同步锁lock数据之前,master发生了宕机。
  3. 哨兵模式从2个slave节点中选举出一个节点作为master节点。
  4. 线程2 在新的master节点获取锁lock成功。
  5. 此时就有两个不同的线程同时持有同一把锁,破坏了锁的互斥性。

目前有两种方法解决主从不一致问题:redis的RedLock和zookepper的分布式锁。

2. 使用redis的分布式锁RedlocK解决主从数据不一致问题。--不推荐

  1. RedLock(红锁):不能只在一个redis实例上创建锁,应该是在多个(n/2+1)redis实例上创建锁,避免在一个redis实例上加锁。

1Cbkyg7hsD.jpg

  1. RedLock在实际项目中很少用到,因为它的缺点很多。
  • 使用RedLock之后,实现变复杂。
  • 需要提供多个独立的redis节点,在高并发情况下,性能会变得很差。
  • 运维成本高,维护繁杂。

redis出现节点宕机的情况是极低的,而且redis集群的思想是AP(Availability, Partition tolerance)思想,优先保证高可用性,可以想办法做到最终一致性。如果一定要保证主从一致性,推荐使用zookeeper,它是CP(Consistency, Partition tolerance)思想。

3. 使用zookeeper的分布式锁解决主从数据不一致问题。--推荐

四、代码演示

加锁、设置过期时间等操作都是在一个LUA脚本中完成的,保证了多条命令执行的原子性。