Redis主从切换,锁失效 怎么办?
1.Redis主从架构的分布式锁的执行流程
在Redis主从架构中,写入的都是 master Redis实例,master 主实例会向 slave 从实例同步key。
一个业务线程 通过向主Redis实例中写入 key-value 来实现加分布式锁,加锁后开始执行业务代码。
具体如下图所示:

当前,这里也涉及到一个核心问题: 锁过期了,业务还没执行完, 怎么办?
锁过期问题,大概的解决方案 有2种:
1:模拟CAS乐观锁的方式,增加版本号
2:watch dog自动延期机制(推荐)
现在聚焦的问题: master Redis实例挂掉了,slave 从Redis 还没有完成复制,导致 Redis分布式锁失效,怎么办?
2.master 挂了但slave 没有完成复制,锁失效了,怎么办?
一般情况下:如果主master Redis实例挂掉了,会选举出一个从Redis实例成为主的。这是redis 集群的故障转移机制。
但是,如果刚刚加锁的key还没有来得及同步到slave Redis中,新选出的主Redis实例中就没有这个key,这个时候业务线程B就能加锁来获取分布式锁,导致锁失效了。线程B 加锁成功,也执行业务代码了。
而这个时候A还没有执行结束,所以就会出现并发安全问题,这就是Redis主从架构下的分布式锁失效问题。
3.Redis 分布式锁的高可用方案
本质上,Redis 分布式锁的高可用,有两个层面的解决方案:
3.1.Server端 的高可用方案
Redis 分布式锁的Server端高可用方案, 就是通过配置, 保证Server 尽量可能少的数据丢失。
在redis的配置文件中有两个参数我们可以设置:
1 | min-slaves-to-write 1 |
min-slaves-to-write默认情况下是0,min-slaves-max-lag默认情况下是10。
min-slaves-to-write:设置主库最少得有 N 个健康的从库存活才能执行写命令。这个配置虽然不能保证 N 个从库都一定能接收到主库的写操作,但是能避免当没有足够健康的从库时,主库无法正常写入,以此来避免数据的丢失。min-slaves-max-lag:配置从库和主库进行数据复制时的 ACK 消息延迟的最大时间,可以确保从库在指定的时间内,如果 ACK 时间没在规定时间内,则拒绝写入。
以上面配置为例,这两个参数表示至少有1个salve的与master的同步复制延迟不能超过10s,一旦所有的slave复制和同步的延迟达到了10s,那么此时master就不会接受任何请求。
可以减小min-slaves-max-lag参数的值,这样就可以避免在发生故障时大量的数据丢失,一旦发现延迟超过了该值就不会往master中写入数据。
配置了 Server端 的高可用方案, 那么对于client,可以设计好合理的降级措施。
如果 Server端 不可用,需要进行及时预警和合理的降级。 比如,把redis 锁降级为 Zookeeper 分布式锁。
从 CAP定理来说, Redis集群倾向AP(高并发),ZP集群则倾向CP(高可用)。
从写入的流程上来说:
- 在向Redis集群里的主结点写入数据时,写入主节点就立刻告诉客户端写入成功。
- 而在向ZK的主结点写入数据时,并不是立刻告诉客户端写入成功,而是先同步给从结点,至少半数的节点同步成功才能返回“写入成功”给客户端。
这个时候如果ZK的主节点挂了,ZK的ZAB分布式一致性协议能保证一定是数据同步完成的结点被选举为主节点,所以ZK 不会发生分布式锁的失效问题。
但是,ZK是低性能的方案。
3.2.Client 端 的高可用方案
如果不改用ZK,就是要用Redis Client 端 方案来解决主从架构的分布式锁失效问题。
Client 端 的高可用方案,就是使用 红锁(RedLock)。
什么是RedLock ? 红锁(RedLock) 的设计,就是从 client 客户端 解决了单一 Redis 实例作为分布式锁可能出现的单点故障问题。
红锁(RedLock)是一种分布式锁算法,由 Redis 的作者 Salvatore Sanfilippo(也称为 Antirez)设计,用于在分布式系统中实现可靠的锁机制。
红锁(RedLock)实现原理:
- 多节点加锁: RedLock 不在单个 Redis 实例上加锁,而是在多个独立的 Redis 实例上同时尝试获取锁。通常建议使用奇数个 Redis 实例(如 5 个),以确保系统具有较好的容错性。
- 多数节点同意: 系统只有在获得了大多数 Redis 实例的锁(即 N/2 + 1 个节点,N 为节点总数)之后,才认为成功获取了分布式锁。这样即使部分 Redis 实例发生故障,整体锁服务仍然可用。
- 时间同步: 为防止客户端在持有锁的过程中发生故障而导致锁无法释放,RedLock 会在获取锁时设置一个超时时间。如果客户端在锁超时之前未能完成任务并释放锁,其他客户端可以在锁超时后重新尝试获取。
- 锁释放: 释放锁时,客户端需要向所有 Redis 实例发送释放锁的命令,以确保所有实例上的锁都被清除。
首先要有多个(最好是奇数个)对等的(没有主从关系)Redis结点。
当进行加锁时(比如是用SETNX命令),则这个设置key-value的命令会发给每个Redis结点执行,当且仅当客户端收到超过半数的结点写成功的消息时,才认为加锁成功,才开始执行后面的业务代码。
红锁(RedLock)具体应用:
红锁(RedLock)工作流程,总结如下:
- 客户端尝试顺序地向所有 Redis 实例发送加锁命令。
- 对于每个实例,客户端尝试在指定的超时时间内获取锁。
- 客户端计算已经成功加锁的实例数量,如果达到多数(N/2 + 1),则认为客户端成功获取了分布式锁。
- 如果获取锁失败,客户端需要向所有实例发送释放锁的命令,以避免留下未释放的锁。