美文网首页
redis Distributed locks(7)

redis Distributed locks(7)

作者: lmem | 来源:发表于2017-05-29 18:18 被阅读179次

    1.需要保证的属性

    Safety property: Mutual exclusion(互斥). At any given moment, only one client can hold a lock.
    Liveness property A: Deadlock free. Eventually it is always possible to acquire a lock, even if the client that locked a resource crashes or gets partitioned.
    Liveness property B: Fault tolerance. As long as the majority of Redis nodes are up, clients are able to acquire and release locks.

    2.基于备份的实现无法保证

    //在实例中创建一个key,用于lock
    The simplest way to use Redis to lock a resource is to create a key in an instance. The key is usually created with a limited time to live, using the Redis expires feature, so that eventually it will get released (property 2 in our list). When the client needs to release the resource, it deletes the key.
    //问题,如果master死了,那么无法保证,因为副本是异步的
    Superficially this works well, but there is a problem: this is a single point of failure in our architecture. What happens if the Redis master goes down? Well, let’s add a slave! And use it if the master is unavailable. This is unfortunately not viable. By doing so we can’t implement our safety property of mutual exclusion, because Redis replication is asynchronous.
    There is an obvious race condition with this model:

    • Client A acquires the lock in the master.
    • The master crashes before the write to the key is transmitted to the slave.
    • The slave gets promoted to master.
    • Client B acquires the lock to the same resource A already holds a lock for. SAFETY VIOLATION!

    3.Correct implementation with a single instance

    只是对于单实例,不适用集群
    To acquire the lock, the way to go is the following:
    //设置key的时候,只有不存在NX选项

    SET resource_name my_random_value NX PX 30000
    

    The command will set the key only if it does not already exist (NX option), with an expire of 30000 milliseconds (PX option). The key is set to a value “myrandomvalue”. This value must be unique across all clients and all lock requests.
    Basically the random value is used in order to release the lock in a safe way, with a script that tells Redis: remove the key only if it exists and the value stored at the key is exactly the one I expect to be. This is accomplished by the following Lua script:

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

    //有效避免了,锁的冲突,安全性问题
    This is important in order to avoid removing a lock that was created by another client. For example a client may acquire the lock, get blocked in some operation for longer than the lock validity time (the time at which the key will expire), and later remove the lock, that was already acquired by some other client. Using just DEL is not safe as a client may remove the lock of another client. With the above script instead every lock is “signed” with a random string, so the lock will be removed only if it is still the one that was set by the client trying to remove it.


    4.The Redlock algorithm

    redis的分布式算法,使用N个完全独立Master,而不是基于复制的模式
    In the distributed version of the algorithm we assume we have N Redis masters. Those nodes are totally independent, so we don’t use replication or any other implicit coordination system. We already described how to acquire and release the lock safely in a single instance. We take for granted that the algorithm will use this method to acquire and release the lock in a single instance. In our examples we set N=5, which is a reasonable value, so we need to run 5 Redis masters on different computers or virtual machines in order to ensure that they’ll fail in a mostly independent way.
    In order to acquire the lock, the client performs the following operations:

    • (1)It gets the current time in milliseconds.
      获取时间戳
    • (2)It tries to acquire the lock in all the N instances sequentially, using the same key name and random value in all the instances. During step 2, when setting the lock in each instance, the client uses a timeout which is small compared to the total lock auto-release time in order to acquire it. (设置一个timeout过期时间)For example if the auto-release time is 10 seconds, the timeout could be in the ~ 5-50 milliseconds range. This prevents the client from remaining blocked for a long time trying to talk with a Redis node which is down: if an instance is not available, we should try to talk with the next instance ASAP.
    • (3)The client computes how much time elapsed in order to acquire the lock, by subtracting from the current time the timestamp obtained in step 1. If and only if the client was able to acquire the lock in the majority of the instances (at least 3), and the total time elapsed to acquire the lock is less than lock validity time, the lock is considered to be acquired.(只有当大部分的master同意,并且获取锁的时间小于锁的有效时间)
    • (4)If the lock was acquired, its validity time is considered to be the initial validity time minus the time elapsed, as computed in step 3.
    • (5)If the client failed to acquire the lock for some reason (either it was not able to lock N/2+1 instances or the validity time is negative), it will try to unlock all the instances (even the instances it believed it was not able to lock).(如果获取失败会释放所有masters的锁定)

    5. 要点

    (1)Retry on failure 当获取锁失败的时候要快速重试,减少裂变的可能!!

    When a client is unable to acquire the lock, it should try again after a random delay in order to try to desynchronize multiple clients trying to acquire the lock for the same resource at the same time (this may result in a split brain condition where nobody wins). Also the faster a client tries to acquire the lock in the majority of Redis instances, the smaller the window for a split brain condition (and the need for a retry), so ideally the client should try to send the SET commands to the N instances at the same time using multiplexing.

    https://redis.io/topics/distlock

    相关文章

      网友评论

          本文标题:redis Distributed locks(7)

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