Redis study notes 01- Distributed Lock

1. Distributed Lock understood definition of


In a concurrent task, when performing modification and deletion of data in order to prevent multiple tasks at the same time get the data generated confusion, then we should use distributed lock to restrict the concurrent execution of the program.
Redis distributed lock on target to be achieved is the essence of being declared a territory in Redis years, when other processes to use this territory, a process has been found in the possession of this territory had to choose to give up or wait.

2.Redis use of distributed lock


Declared a territory usually use setnx (set if not exists) instructions and may only be occupied by a client in Redis. First come first served basis, call the del command to leave the territory upon completion of use.

    >setnx island-1 ll
    (integer) 1
    >setnx island-1 pp
    (integer) 0
    >get island-1
    "ll"
    >del island-1
    (integer) 0
    >get island-1
    (nil)          


But this use will be some problems, if the process of implementation to account for the territory of the half, resulting in abnormal del instructions can not call subsequent to the release, this will cause a deadlock phenomenon, that piece of territory would have been occupied, lock He has been unable to release.
At this point it is common practice to add a lock to the expiration date, such as 5s (expire key 5), so that even appeared in the middle of an exception can also ensure that the lock will be released automatically after 5s.

    >setnx island-2 ll
    ok
    >expire island-2 5
    >get island-2
    "ll"
    ...5s之后...
    >get island-2
    (nil)


But you do still have problems like this, if the program expire between setnx instructions and instructions to hang as a sudden power failure or human operation, then the same may cause deadlock. The root problem is setnx and expire command is not executed simultaneously.
The general idea might think of using transactions to solve, but unfortunately this method is not feasible because expire is dependent on the results of the implementation of the setnx, if setnx enclosure fails, expire can not be executed, and the characteristics of the transaction is either all executed or not executed. Fortunately add extended instruction set parameters in redis2.8 future releases, so that this problem can be solved.

    >set island-2 ll ex 5 nx
    ok
    >get island-2
    "ll" 
    ... 5s之后...
    >get island-2
    (nil)


Wherein nx (if not exists), ex i.e. expire

3. Redis distributed lock extension

3.1 Timeout problems


redis distributed lock timeout can not solve the problem, because if the execution time between lock and release the lock so long that exceeded the timeout limit is set, it will lead to a first logical thread of execution has not been completed, other threads will be hijacked to the lock. To avoid this, Redis distributed lock generally not used for a long time task.

3.2 reentrancy


Reentrant means that in the case of a request previously held locks lock again, if a lock in the same thread support this feature, the lock is reentrant. Redis distributed lock in order to achieve reentrancy, it is necessary to set the method for packaging the client, Threadlocal variables to store the thread count currently hold locks. python version of the code is as follows:

    import redis
    import threading


    locks = threading.local()
    locks.redis = {}

    def key_for(user_id):

    return 'account_{}'.format(user_id)

    # 加锁
    def _lock(client,key):

        return bool(client.set(key,True,nx=True,ex=5))
    # 解锁
    def _unlock(client,key):

        client.delete(key)

    # 执行加锁 + 计数
    def lock(client,user_id):
        key = key_for(user_id)
        if key in locks.redis:
            locks.redis[key] += 1
            return True
        ok = _lock(client,key)
        if not ok:
            return False
        locks.redis[key] = 1
        return True

    # 执行解锁 + 计数
    def unlock(user_id):
         key = key_for(user_id)
         if key in locks.redis:
             locks.redis[key] -= 1
             if locks.redis[key] <= 0:
                 del locks.redis[key]
                 return True
         return False


    client = redis.StrictRedis()
    print('lock',lock(client,'ll'))    # lock True
    print('lock',lock(client,'ll'))    # lock True
    print('unlock',unlock('ll'))      # unlock False  未完全解锁
    print('unlock',unlock('ll'))      # unlock False


This is not a precise reentrant lock, you can also add an expiration time, and so on, but the complexity of the code will always increase, so does not recommend the use of reentrant lock.

-------------------------------------------------- ---------------------------------------
article reference to "Redis depth Adventures: core principles and application practice "- author: Qian product
---------------------------------------- -------------------------------------------------

Guess you like

Origin www.cnblogs.com/LLBoy/p/11457540.html