潜在风险产生原因
- 网络延迟:
- 在获取锁时,客户端向 Redis 发送获取锁的命令,若网络出现延迟,Redis 可能已经成功分配锁,但客户端未及时收到响应。此时客户端可能会再次尝试获取锁,造成重复获取锁的情况。
- 在释放锁时,客户端向 Redis 发送释放锁的命令,若网络延迟,命令可能未及时到达 Redis 服务器,而客户端认为锁已释放,导致在其他客户端还持有锁的情况下,本客户端误以为锁已释放,后续可能再次获取锁,造成锁的误释放。
- 时钟漂移:
- 分布式系统中,各个服务器的时钟可能存在微小差异(时钟漂移)。如果使用基于时间的锁机制(如设置锁的过期时间),当一台服务器时钟比其他服务器快时,它可能过早地认为锁已过期并释放锁,而实际上其他服务器上的客户端仍在持有该锁进行操作,导致锁的误释放。
解决方案
- 使用 Lua 脚本:
- 原理:Lua 脚本在 Redis 中是原子执行的。可以将锁的获取和释放逻辑写在 Lua 脚本中。例如,获取锁时检查锁是否存在且值为当前客户端的唯一标识,若存在则获取成功;释放锁时,只有当锁的值为当前客户端唯一标识时才执行删除操作。
- 示例 Lua 脚本(释放锁):
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
- 优点:通过 Lua 脚本的原子性,避免了网络延迟造成的误释放风险,因为整个操作要么全部执行,要么全部不执行。
- 引入 Redlock 算法:
- 原理:Redlock 算法基于多个独立的 Redis 实例(至少 5 个)来实现分布式锁。客户端尝试在多个实例上获取锁,只有当在大多数实例(至少 (N / 2) + 1,N 为实例总数)上成功获取锁时,才认为获取锁成功。释放锁时,需要在所有实例上释放锁。
- 优点:由于是基于多个实例,即使某个实例因时钟漂移或网络延迟出现问题,也不会影响整个锁机制的正确性,提高了锁的可靠性,降低了误释放风险。
- 延长锁的过期时间并定期续约:
- 原理:在获取锁时,设置一个较长的过期时间,同时客户端开启一个定时任务,在锁过期时间到达前,检查锁是否仍由自己持有,如果是,则延长锁的过期时间(续约)。
- 优点:通过延长过期时间,减少了因时钟漂移导致锁提前过期的可能性,而定期续约机制保证了在业务处理时间较长时,锁不会意外过期被释放。