Redis分布式锁的原理、问题及解决方案:
Redis分布式锁是通过 SET lock $uuid EX 20 NX 方法来进行加锁
Redis 2.6.12 之后,Redis 扩展了 SET 命令的参数,把 NX/EX 集成到了 SET 命令中,用这一条命令就可以了
EX是设置存活时间,NX是只有没有这个key才进行操作
加锁后还需要一个守护线程,去定时延长这个key的存活时间,不然会存在代码没有执行完毕,锁释放了的情况
在Redisson中,被称为看门狗机制
之所以把锁的Value设置为UUID(唯一标识),是为了防止其他线程将锁释放
所以在释放锁的时候,得先判断锁是不是自己的锁(唯一标识一不一致),是自己的锁才释放
这里又会有一个问题,判断和解锁存在原子性问题,可能存在,判断的时候是自己的锁,释放的时候不是自己的锁了
这个我们可以通过编写Lua脚本,来规避这个问题,因为Redis是单线程的
不过即使这样,在主从集群+哨兵模式下,还是会存在问题
Redis主从集群+哨兵模式的原理、问题及解决方案:
主从集群是通过主从复制进行同步数据的,主从复制是一个异步请求
当客户端在主库中加锁后,主库宕机了,最新数据还没同步到从库上,这个时候哨兵模式会实现故障自动切换,将从库被提为主库,继续提供服务,但是当前主库的锁就丢失了,还是会引起安全性问题
这个问题是因为主库宕机时产生的,所以官方的解决方案是使用RedLock解决这个问题
RedLock的原理、问题:
RedLock是部署多台Redis实例,每台Redis都是相互独立的主库,假设部署了5台Redis,分别为A、B、C、D、E
当客户端发起加锁请求之前,先记录当前的时间T1
然后再向各个Redis发起加锁请求,每次请求设置一个过期时间(这个时间要远小于锁的有效时间),一个一个Redis请求
如果加锁成功的Redis实例有大多数(大于等于3)都加锁成功时,记录当前时间T2,当T2-T1<锁的过期时间时,视为加锁成功,否则视为加锁失败
这里判断时间的目的是:为了防止当A加锁成功后,请求B时,发生了网络延迟或者进程GC,导致请求B的时间比锁的过期时间还长,致使A的锁自动释放了,另外一条线程又来拿A的锁是可以正常拿到的,之前那个线程也认为自己拿到了A的锁,从而出现的问题
加锁失败,或者加锁成功处理完业务后,需要对所有的Redis实例进行释放锁处理
不过即使这样处理,还是会存在,当拿到锁后,修改共享资源时,出现NPC,还是会出现安全性问题
N:Network Delay,网络延迟
P:Process Pause,进程暂停(GC)
C:Clock Drift,时钟漂移
总结:
1.分布式锁不是100%安全的
2.在修改敏感数据时,需要资源层进行兜底,例如乐观锁
参考资料
https://cloud.tencent.com/developer/inventory/52658/article/2332108