美文网首页
正确的Redis分布式锁实现(Distributed lock)

正确的Redis分布式锁实现(Distributed lock)

作者: AlanSun2 | 来源:发表于2019-06-18 16:53 被阅读0次

    锁要实现的三个目标:

    1. 相互排斥。在任何给定时刻,只有一个客户端可以持有锁。
    2. 无死锁。最终,即使锁定资源的客户端崩溃或被分区,也始终可以获取锁定。
    3. 容错。只要大多数Redis节点启动,客户端就能够获取和释放锁。
    1. 获取锁:
      SET resource_name my_random_value NX PX 30000
    

    该命令仅在密钥尚不存在时才设置key(NX选项),到期时间为30000毫秒(PX选项)。键设置为值“myrandomvalue”。此值必须在所有客户端和所有锁定请求中都是唯一的。

    • myrandomvalue:必须当前唯一
    • 30000:30000毫秒是你yu
    2. 释放锁

    使用随机值是为了以安全的方式释放锁,实现上使用了Redis的脚本:只有当key存在且其值恰好是我期望的随机值时才删除key。这是通过以下Lua脚本完成的:

    if redis.call("get",KEYS[1]) == ARGV[1] then
        return redis.call("del",KEYS[1])
    else
        return 0
    end
    

    这一点很重要,这可以避免删除由另一个客户端创建的锁。例如:

    1. 线程A获取锁,由于某些原因A线程挂起了。时间大于锁的过期时间。
    2. 锁过期后,线程B获取锁。
    3. 线程A恢复以后,处理完相关事件,向redis发起 del命令。锁被释放。
    4. 线程C获取锁。这个时候一个系统中同时两个线程(B和C)持有锁。

    ok,这样我们完成redis分布式锁的实现。你以为这样就安全了吗,接下来我们设想这样一个情况:

    首先,假设我们的Redis是主从的:

    • 如果在线程A在master上拿到了锁。
    • 在master将数据同步到slave上之前,master宕机。
    • 线程B就从slave上又一次拿到了锁。

    这时就会同时有两把锁存在的情况。对于这种情况(非常特殊),Redis官方提供了一个叫RedLock的解决方案。

    RedLock

    我们假设我们有N个Redis主机。这些节点完全独立,因此我们不使用复制或任何其他隐式协调系统。我们假设N = 5,这是一个合理的值,因此我们需要在不同的计算机或虚拟机上运行5个Redis主服务器,以确保它们以大多数独立的方式失败。

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

    1. 获取当前时间的毫秒值。
    2. 它尝试使用相同的键名和随机值按顺序获取所有N个实例中的锁。在步骤2期间,当在每个实例中设置锁定时,客户端使用与总锁定自动释放时间相比较小的超时以获取它(客户端超时时间,并不是key的过期时间)。例如,如果自动释放时间是10秒,则超时可以在~5-50毫秒范围内。这可以防止客户端长时间保持阻塞状态,试图与已关闭的Redis节点通信:如果一个实例不可用,我们应该尝试尽快与下一个实例交谈。
    3. 客户端通过从当前时间中减去在步骤1中获得的时间戳来计算获取锁定所经过的时间。当且仅当客户端能够在大多数实例(至少3)中获取锁,并且获取锁所经过的总时间小于锁定有效时间时,才认为锁定被获取。
    4. 如果获得了锁,则其有效时间被认为是初始有效时间减去经过的时间,如步骤3中计算的那样。
    5. 如果客户端由于某种原因(无法锁定N / 2 + 1实例或有效时间为负)未能获取锁定,它将尝试解锁所有实例(即使它认为不是能够锁定)。

    我个人觉得对于小公司这还是相当昂贵的。

    其他的细节请参考redis官方文档

    相关文章

      网友评论

          本文标题:正确的Redis分布式锁实现(Distributed lock)

          本文链接:https://www.haomeiwen.com/subject/ikpfqctx.html