Redis 分布式锁详解( 六 )


为了获取锁 , 客户端执行以下操作:

  1. 以毫秒为单位获取当前时间 。
  2. 使用相同的 key 和随机值在所有 Redis 实例中顺序获取锁 。当在每个实例中获取锁时 , 客户端使用一个超时 , 该超时与锁自动释放的总时间相比很小 , 以便获取它 。例如 , 如果自动释放时间为 10 秒 , 则超时时间可能在 5-50 毫秒范围内 。这可以防止客户端在尝试与已关闭的 Redis 节点通话时长时间处于阻塞状态:如果某个实例不可用 , 我们应该尽快尝试与下一个实例通话 。
  3. 客户端通过从当前时间中减去在步骤 1 中获得的时间戳来计算获取锁所用的时间 。当且仅当客户端能够在大多数实例(至少 3 个)中获取锁 , 并且获取锁所用的总时间小于锁有效时间 , 则认为已获取锁 。
  4. 如果获得了锁 , 其有效时间将被视为初始有效时间减去经过的时间 , 如步骤 3 中计算的 。
  5. 如果客户端由于某种原因(无法锁定 N/2+1 实例或有效期为负)未能获取锁 , 它将尝试解锁所有实例(即使是它认为无法锁定的实例) 。
Redis 作者对红锁的介绍非常详细 , 点击这里查看 。
简单总结下:
假设有五个 Redis 实例 , 这些实例之间是完全独立的 , 并且部署在不同的计算机上 , 客户端尝试在这几个实例中获取锁 。
如果客户端能够在大多数实例(N/2+1 , 至少三个)中获取锁 , 并且获取锁所有的总时间小于锁有效时间 , 则认为获取锁成功 。
如果加锁成功 , 锁的有效期=初始有效时间-获取锁的总时间 , 假如锁有效期为 10s , 获取锁共花了 2s , 那么锁的有效期还剩 8s 。
无论客户端获取锁成功还是失败 , 都需要解锁所有 Redis 实例 , 以免发生死锁 。
Redis 分布式锁详解

文章插图
 
使用多个完全独立的 Redis 实例 , 解决了 Redis 主从异步复制造成的锁丢失问题 , 同时保障了高可用 。
至少 N/2+1 个实例加锁成功 , 保证锁的互斥性 , 防止多个客户端同时获取到锁 。
2. Redlock 存在问题表面上看 RedLock 解决 Redis 分布式锁的痛点 , 但是真的就万无一失了吗?
有人就提出了质疑 , Martin Kleppmann: How to do distributed locking
Martin Kleppmann 在效率和正确性方面质疑了红锁 , 他认为如果是为了效率使用分布式锁 , 没有必要承担 Redlock 的成本和复杂性 , 最好还是使用一个 Reids 实例或者主从模式 。正确性方面 , 他认为 Redlock 也绝对保证不了锁的正确性 , 文章在网络延迟 , 过程暂停(GC) , 时钟漂移方面给出了论证 。
Redis 作者(Salvatore)也反驳了该质疑:Is Redlock safe?
建议大家读下上面两篇文章 。
我个人认为使用 Redlock 要慎重 , 首先 , 它的效率比较差 , 在一些 RT 要求比较高的接口中增加了耗时风险;其次 , 无法保证绝对的正确性 , 可能会出现多个客户端同时获取锁的风险(Martin Kleppmann 在他的文章里有举证);再次 , 成本和复杂性较高 。
3. Redisson红锁使用使用示例:
// 在不同Redis实例上获取 RLockRLock rLock1 = redisson1.getLock(key);RLock rLock2 = redisson2.getLock(key);RLock rLock3 = redisson3.getLock(key);// 初始化红锁RedissonRedLock redissonRedLock = new RedissonRedLock(rLock1, rLock2, rLock3);// 加锁redissonRedLock.lock();// 业务逻辑// 解锁redissonRedLock.unlock();复制代码
Redis 分布式锁详解

文章插图
 
Redisson 在新版本中已经弃用了 RedissonRedLock , 不建议使用 。

【Redis 分布式锁详解】


推荐阅读