这里我参考了 码哥字节的公众号文章。个人认为文章上讲的很全了。但是我还是要自己整理一下。对于Redis实现分布式锁,具体使用还是用三方Redission客户端的实现吧。但是我们还是要知道一下实现的细节。
对于单例Redis和分布式部署的Redis在实现锁的算法上还是不同的。分布式Redis上实现起来的算法更为负责
单例Redis分布锁实现过程
这里我先解释一下啥是单例Redis,就是单独启动的一个Redis。没有其他花里胡哨的东西。
方案1
- 实现方法
- 加锁:通过setnx命令设置一个key
- 解锁:通过del命令删除key
缺点
实现方法
- 加锁:通过 set nx ex 命令设置一个key
- 解锁:通过 del命令删除key
- 改进点
- 不会出现锁永远解不开的情况
缺点
实现方法
- 加锁:通过 set nx ex 命令设置一个key,并且设置一个能唯一标识本次执行的 value,比如uuid
- 解锁:通过get获取锁,得到存入锁中的value值,若和当前值相等 执行del
- 改进点
- 大大降低了出现误删的情况
- 缺点
可重入锁实现
以上的方式都不是可重入的,码哥字节在文章中说过,可以通过RedisHash结构实现(Redission其实也是这样实现的)
主从架构带来的问题
上面的方案4是在单例Redis下基本上不存在问题的一种。但是将其放在主从架构的Redis部署时,也会出现问题。
可以参考以下下面场景
- A线程在Redis上加锁成功,开始运行自己的程序
- 此时Redis的master节点挂了,并且锁还未同步到slave节点
- slave节点顶替了master节点。
- 此时B线程也来请求锁,此时的master节点(原slave节点)也会为其上锁
- 于是导致A线程和B线程同时拥有这个锁了
如何解决分布式Redis下的锁问题
通过红锁来实现,但是具体是否能解决主从以及在当前主流集群部署下加锁的正确性。作者也不知道。且RedLock这个概念也在争议中。所以下面我就简单说一下这个概念。不做个人理解
- 客户端获取当前时间戳 T1
- 使用key-value在N个Redis实例上依次尝试获取锁
- 每个实例上等待锁的时间都要远远小于锁自动释放的时间
- 官方给出例子:若10s自动释放,等待锁的时间要控制在5~50ms
- 客户端遍历所有的实例后,计算设置锁的时间(T3=当前时间-T1)。满足以下两个条件时加锁成功
- 加锁成功实例个数 >= N/2+1
- T3 小于自动释放锁时间
- 若失败加锁,应该释放加过的锁
Redission实现的分布式锁
Redission 真得是 yyds。这里我只介绍几个实验过来的结果
- Redission完成加锁后,若没有指定自动释放时间,Redission会只给锁30s过期时间,之后每10s重置回30
- 防止服务宕机后,锁不释放问题
- 完成这个操作的东西叫做 看门狗
- 若指定了自动释放锁时间,则不需要看门狗