这里我参考了 码哥字节的公众号文章。个人认为文章上讲的很全了。但是我还是要自己整理一下。对于Redis实现分布式锁,具体使用还是用三方Redission客户端的实现吧。但是我们还是要知道一下实现的细节。
对于单例Redis和分布式部署的Redis在实现锁的算法上还是不同的。分布式Redis上实现起来的算法更为负责

单例Redis分布锁实现过程

这里我先解释一下啥是单例Redis,就是单独启动的一个Redis。没有其他花里胡哨的东西。

方案1

  1. 实现方法
    1. 加锁:通过setnx命令设置一个key
    2. 解锁:通过del命令删除key
  2. 缺点

    1. 加锁的客户端若挂了,加的锁永远不会被释放

      方案2

  3. 实现方法

    1. 加锁:通过 set nx ex 命令设置一个key
    2. 解锁:通过 del命令删除key
  4. 改进点
    1. 不会出现锁永远解不开的情况
  5. 缺点

    1. 会出现锁误删情况:A线程执行时间过长,锁已经自动释放,此时B线程获取锁,A线程此时执行完了,稀里糊涂把B线程的锁给删除了。

      方案3

  6. 实现方法

    1. 加锁:通过 set nx ex 命令设置一个key,并且设置一个能唯一标识本次执行的 value,比如uuid
    2. 解锁:通过get获取锁,得到存入锁中的value值,若和当前值相等 执行del
  7. 改进点
    1. 大大降低了出现误删的情况
  8. 缺点
    1. get和del非原子性的,所以还是有误删的可能

      方案4

      方案4就是在方案3解锁步骤通过 LUA脚本实现即可。

可重入锁实现

以上的方式都不是可重入的,码哥字节在文章中说过,可以通过RedisHash结构实现(Redission其实也是这样实现的)

主从架构带来的问题

上面的方案4是在单例Redis下基本上不存在问题的一种。但是将其放在主从架构的Redis部署时,也会出现问题。
可以参考以下下面场景

  1. A线程在Redis上加锁成功,开始运行自己的程序
  2. 此时Redis的master节点挂了,并且锁还未同步到slave节点
  3. slave节点顶替了master节点。
  4. 此时B线程也来请求锁,此时的master节点(原slave节点)也会为其上锁
  5. 于是导致A线程和B线程同时拥有这个锁了

如何解决分布式Redis下的锁问题

通过红锁来实现,但是具体是否能解决主从以及在当前主流集群部署下加锁的正确性。作者也不知道。且RedLock这个概念也在争议中。所以下面我就简单说一下这个概念。不做个人理解

  1. 客户端获取当前时间戳 T1
  2. 使用key-value在N个Redis实例上依次尝试获取锁
    1. 每个实例上等待锁的时间都要远远小于锁自动释放的时间
    2. 官方给出例子:若10s自动释放,等待锁的时间要控制在5~50ms
  3. 客户端遍历所有的实例后,计算设置锁的时间(T3=当前时间-T1)。满足以下两个条件时加锁成功
    1. 加锁成功实例个数 >= N/2+1
    2. T3 小于自动释放锁时间
  4. 若失败加锁,应该释放加过的锁

Redission实现的分布式锁

Redission 真得是 yyds。这里我只介绍几个实验过来的结果

  1. Redission完成加锁后,若没有指定自动释放时间,Redission会只给锁30s过期时间,之后每10s重置回30
    1. 防止服务宕机后,锁不释放问题
    2. 完成这个操作的东西叫做 看门狗
  2. 若指定了自动释放锁时间,则不需要看门狗

参考文档