redlock分布式锁

简介

什么是readlock

简单来说就是Readlock是redis实现分布式锁的一种方式.但不同点在于ReadLock是Redis坐着Antirez在单Redis节点基础上引入的课可用模式

为什么要使用 RedLock

在 【Redis】之分布式锁 中我也指出了,不管使用原生的 SET key value EX NX 命令还是使用 Redisson 这个能有效解决锁续期的方案,它们都无法解决 Redis 在主从复制、哨兵集群下的多节点问题:

客户端 A 将 Key 写入到 Master 节点成功获取到锁;
此时 Master 节点发生故障,Key 没有来得及同步到 Slave 上(数据是后台通过异步同步的);
Slave 节点升级为 Master 节点;
客户端 B 从新的 Master 节点获取到了对应同一个资源的锁。
这种情况下锁的安全性被打破了,所以 Redis 的作者就实现了解决这种问题的 RedLock 锁。

RedLock 加锁原理

在Redis分布式集群中,假设我们有5个redis节点(中小规模项目一般是1主4从+3哨兵)则RedLock枷锁过程如下

  1. 获取当前Unix事件,以毫秒为单位,并设置锁超时时间TTL(TTL时间要大于正常业务执行的时间+成功获取锁消耗的时间+时钟漂移)
  2. 一次从5个节点获取锁,需要使用相同的key和具有唯一性的value.获取锁时,需要设置一个网络连接和响应超时时间,这个超时时间要小于锁的失效时间TTL,从而避免客户端死等.比如TTL为5s,设置获取锁最多用1s,如果1s无法获取锁,就放弃获取这个锁,尝试从下个节点获取锁
  3. 客户端获取所有的锁后的事件减去第一步事件,就得到了获取锁消耗的时间(锁的获取时间要小于锁的失效时间 TTL,并且至少从半数以上 (N/2+1)的Redis节点取到锁,才算获取成功锁);
  4. 成功获得锁后,key 的真正有效时间 = TTL - 锁的获取时间 - 时钟漂移。比如:TTL 是10s,获取所有锁用了 2s,则真正锁有效时间为 8s;
  5. 如果获取锁失败(没有在半数以上实例取到锁或者取锁时间已经超过了有效时间),客户端应该在所有的 Redis 实例上进行解锁,即使是没有加锁成功的 Redis 实例;
  6. 失败重试:当客户端获取锁失败时,应该在随机时间后重试获取锁;同时重试获取锁要有一定次数限制(在随机时间后进行重试,主要是防止过多的客户端同时尝试去获取锁,导致彼此都获取锁失败的问题);

实现可重入

问题

系统服务不能假定所有的客户端都表现的符合预期。从安全角度讲,服务端必须防范这种来自客户端的滥用。

  1. 客户端 1 从 Redis 节点 A, B, C 成功获取了锁。由于网络问题,无法访问 D 和 E。
  2. 节点 C 上的时钟发生了向前跳跃,导致它上面维护的锁过期了。
  3. 客户端 2 从 Redis 节点 C, D, E 成功获取了同一个资源的锁。由于网络问题,无法访问 A 和 B。
  4. 现在,客户端 1 和客户端 2 都认为自己持有了锁。

这样的场景是可能出现的,因为 Redlock 严重依赖系统时钟,所以一旦系统的时间变得不准确了,那么该算法的安全性也就得不到保障了。

或者

  1. 客户端 1 向 Redis 节点 A, B, C, D, E 发起锁请求。
  2. 各个 Redis 节点已经把请求结果返回给了客户端 1,但客户端 1 在收到请求结果之前进入了长时间的 GC 阶段。
  3. 长时间的 GC,导致在所有的 Redis 节点上,锁过期了。
  4. 客户端 2 在 A, B, C, D, E 上申请并获取到了锁。
  5. 客户端 1 从 GC 阶段中恢复,收到了前面第 2 步来自各个 Redis 节点的请求结果。客户端 1 认为自己成功获取到了锁。
  6. 客户端 1 和客户端 2 现在都认为自己持有了锁。
Last modification:November 17, 2023
如果觉得我的文章对你有用,请随意赞赏