Redis分布式锁问题

通过SET原子操作来设置key和过期时间

// 加锁
// 如果key不存在,那么设置它的值,否则什么也不做
SETNX lock 1
// 10s后自动过期
EXPIRE lock 10

// 2者合一,一条命令保证原子性执行
SET lock 1 EX 10 NX

问题1:无法评估准确的加锁时间(自动续期)
问题2:客户端1释放了客户端2持有的锁(保存和判断加锁者信息)

RedLock

Redis一般采用主从集群+哨兵的模式部署,好处在于,当主库异常宕机时,哨兵可以实现故障自动切换,把从库提升为主库。

主从切换场景

客户端1在主库上加锁成功
主库异常宕机,SET命令还未同步到从库上(主从复制是异步的)
从库被哨兵提升为新主库,这个锁在新的主库上丢失了

Redlock解决方案

只部署主库,主库部署多个,推荐至少5个实例,它们之间没有任何关系
步骤:
1.客户端先获取当前时间戳T1
2.客户端依次向这5个Redis实例发起加锁请求,且每个请求会设置超时时间(毫秒级,要远小于锁的有效时间),如果某一个实例加锁失败,就立即向下一个Redis实例申请加锁
3.如果客户端从>=3个(大多数)以上Redis实例加锁成功,则再次获取当前时间戳T2,如果T2-T1<锁的过期时间,此时,认为客户端加锁成功,否则认为加锁失败
4.加锁成功,之后向全部节点发起释放锁请求
加锁失败,向全部节点发起释放锁请求

4 个重点
1. 客户端在多个 Redis 实例上申请加锁
2. 必须保证大多数节点加锁成功
3. 大多数节点加锁的总耗时,要小于锁设置的过期时间
4. 释放锁,要向全部节点发起释放锁请求

1) 为什么要在多个实例上加锁?
为了容错,部分实例异常宕机,剩余的实例加锁成功,整个锁服务依旧可用。
2) 为什么大多数加锁成功,才算成功?
多个 Redis 实例一起来用,组成了一个分布式系统,只要大多数节点正常,那么整个系统依旧是可以提供服务的。
3) 为什么步骤3加锁成功后,还要计算加锁的累计耗时?
因为操作的是多个节点,所以即使大多数节点加锁成功,但如果加锁的累计耗时已经超过了锁的过期时间,那此时有些实例上的锁可能已经失效了,这个锁就没有意义了。

对于Redlock的质疑

分布式系统会遇到的三座大山:NPC
N:Network Delay,网络延迟
P:Process Pause,进程暂停(GC)
C:Clock Drift,时钟漂移

Martin 用一个进程暂停(GC)的例子,指出了 Redlock 安全性问题:
1.客户端 1 请求锁定节点 A、B、C、D、E
2.客户端 1 的拿到锁后,进入 GC(时间比较久)
3.所有 Redis 节点上的锁都过期了
4.客户端 2 获取到了 A、B、C、D、E 上的锁
5.客户端 1 GC 结束,认为成功获取锁
客户端 2 也认为获取到了锁,发生「冲突」

GC 可能发生在程序的任意时刻,而且执行时间是不可控的。

当多个 Redis 节点时钟发生问题时,也会导致 Redlock 锁失效。
1. 客户端 1 获取节点 A、B、C 上的锁,但由于网络问题,无法访问 D 和 E
2. 节点 C 上的时钟「向前跳跃」,导致锁到期
3. 客户端 2 获取节点 C、D、E 上的锁,由于网络问题,无法访问 A 和 B
4. 客户端 1 和 2 现在都相信它们持有了锁(冲突)

提出 fecing token 的方案,保证正确性
1. 客户端在获取锁时,锁服务可以提供一个递增的 token
2. 客户端拿着这个 token 去操作共享资源
3. 共享资源可以根据 token 拒绝后来者的请求

反驳质疑

如果在 1-3 发生了网络延迟、进程 GC 等耗时长的异常情况,那在第 3 步 T2 - T1,是可以检测出来的,如果超出了锁设置的过期时间,那这时就认为加锁会失败,之后释放所有节点的锁就好了!
如果发生网络延迟、进程 GC 是在步骤 3 之后,也就是客户端确认拿到了锁,去操作共享资源的途中发生了问题,导致锁失效,那这不止是 Redlock 的问题,任何其它锁服务例如 Zookeeper,都有类似的问题。
1. 客户端在拿到锁之前,无论经历什么耗时长问题,Redlock 都能够在第 3 步检测出来
2. 客户端在拿到锁之后,发生 NPC,那 Redlock、Zookeeper 都无能为力
Redlock 在保证时钟正确的基础上,是可以保证正确性的。

质疑 fencing token 机制
既然服务器都有了互斥能力,那还要分布式锁干什么?

ZooKeeper分布式锁优缺点

优点
1. 不需要考虑锁的过期时间
2. 加锁失败后,可以watch等待锁释放
缺点
1. 性能不如 Redis
2. 部署和运维成本高
3. 客户端与ZooKeeper的长时间失联,锁被释放问题

参考资料

使用Redis实现分布式锁和ZK实现分布式锁有什么区别,分别有哪些场景?

posted on 2023-07-16 14:33  王景迁  阅读(53)  评论(0编辑  收藏  举报

导航