Process lock (concurrent lock) of redis (seckill)

 

Process lock (concurrent lock) of redis (seckill)

Similar to zookeeper process lock

 

 

While redis is single-threaded this can guarantee serialization of tasks (which can be multi-threaded operations from different processes or multiple concurrent threads of the same redis client),

But there is no guarantee that a thread of a client (a thread of a process) has been purely operating redis. This will result in data loss and modification.

The problem of dirty data, using the process lock of redis can ensure that the thread that acquires the lock of a process will continue to operate until it deletes and releases the lock, which is solved

concurrency problem

This idea can be used to kill pessimistic locks in seconds

 

 

However, spikes can also be implemented with redis things + watcher variables (similar to the optimistic locking point of view)

 

 

 

 

 

Distributed locks can be implemented using the SETNX command of Redis. The implementation method is described below.

Introduction to the SETNX command

command format

SETNX key value

Set the value of key to value if and only if key does not exist. 
If the given key already exists, SETNX does nothing. 
SETNX is shorthand for SET if Not eXists.

return value

Returns an integer, specifically 
- 1, when the value of key is set 
- 0, when the value of key is not set

example

redis> SETNX mykey “hello” 
(integer) 1 
redis> SETNX mykey “hello” 
(integer) 0 
redis> GET mykey 
“hello” 
redis>

Using SETNX to implement distributed locks

Multiple processes execute the following Redis commands:

SETNX lock.foo <current Unix time + lock timeout + 1>

If SETNX returns 1, indicating that the process has acquired the lock, SETNX sets the value of the key lock.foo to the lock timeout (current time + lock valid time). 
If SETNX returns 0, it means that other processes have acquired the lock and the process cannot enter the critical section. A process can keep trying SETNX operations in a loop to acquire a lock.

resolve deadlock

Consider a situation, if the process is disconnected from Redis after acquiring the lock (maybe the process hangs, or the network is interrupted), if there is no effective mechanism to release the lock, then other processes will be in a state of waiting, that is A "deadlock" occurs.

When using SETNX to obtain the lock above, we set the value of the key lock.foo to the effective time of the lock. After the process obtains the lock, other processes will continue to check whether the lock has timed out. If it times out, the waiting process will also have Chance to acquire lock.

However, when the lock times out, we cannot simply use the DEL command to delete the key lock.foo to release the lock. Consider the following situation, process P1 has first acquired the lock lock.foo, and then process P1 dies. Processes P2 and P3 are constantly checking whether the lock has been released or has timed out. The execution flow is as follows:

  • The P2 and P3 processes read the value of the key lock.foo and check whether the lock has timed out (by comparing the current time with the value of the key lock.foo to determine whether the lock has timed out)
  • P2 and P3 processes find that lock lock.foo has timed out
  • P2 executes DEL lock.foo command
  • P2 executes the SETNX lock.foo command and returns 1, that is, P2 acquires the lock
  • P3 executes the DEL lock.foo command to delete the key lock.foo just set by P2 (this step is because P3 has just detected that the lock has timed out)
  • P3 executes the SETNX lock.foo command and returns 1, that is, P3 acquires the lock
  • P2 and P3 acquired the lock at the same time

As can be seen from the above situation, after detecting the lock timeout, the process cannot simply perform the operation of DEL delete key to obtain the lock.

In order to solve the problem that multiple processes may acquire locks at the same time in the above algorithm, let's look at the following algorithm. 
We also assume that process P1 has first acquired the lock lock.foo, and then process P1 dies. What happens next:

  • 进程P4执行 SETNX lock.foo 以尝试获取锁
  • 由于进程P1已获得了锁,所以P4执行 SETNX lock.foo 返回0,即获取锁失败
  • P4执行 GET lock.foo 来检测锁是否已超时,如果没超时,则等待一段时间,再次检测
  • 如果P4检测到锁已超时,即当前的时间大于键 lock.foo 的值,P4会执行以下操作 
    GETSET lock.foo <current Unix timestamp + lock timeout + 1>
  • 由于 GETSET 操作在设置键的值的同时,还会返回键的旧值,通过比较键 lock.foo 的旧值是否小于当前时间,可以判断进程是否已获得锁
  • 假如另一个进程P5也检测到锁已超时,并在P4之前执行了 GETSET 操作,那么P4的 GETSET 操作返回的是一个大于当前时间的时间戳,这样P4就不会获得锁而继续等待。注意到,即使P4接下来将键 lock.foo 的值设置了比P5设置的更大的值也没影响。

另外,值得注意的是,在进程释放锁,即执行 DEL lock.foo 操作前,需要先判断锁是否已超时。如果锁已超时,那么锁可能已由其他进程获得,这时直接执行 DEL lock.foo 操作会导致把其他进程已获得的锁释放掉

程序代码

用以下python代码来实现上述的使用 SETNX 命令作分布式锁的算法。

LOCK_TIMEOUT = 3
lock = 0
lock_timeout = 0
lock_key = 'lock.foo'

# 获取锁 while lock != 1: now = int(time.time()) lock_timeout = now + LOCK_TIMEOUT + 1 lock = redis_client.setnx(lock_key, lock_timeout) if lock == 1 or (now > int(redis_client.get(lock_key))) and now > int(redis_client.getset(lock_key, lock_timeout)): break else: time.sleep(0.001) # 已获得锁 do_job() # 释放锁 now = int(time.time()) if now < lock_timeout: redis_client.delete(lock_key)

java代码:
public static boolean acquireLock(String lock) {
    // 1. 通过SETNX试图获取一个lock
    boolean success = false;
    Jedis jedis = pool.getResource();      
    long value = System.currentTimeMillis() + expired + 1;    
    System.out.println(value);    
    long acquired = jedis.setnx(lock, String.valueOf(value));
    //SETNX成功,则成功获取一个锁
    if (acquired == 1)      
        success = true;
    //SETNX失败,说明锁仍然被其他对象保持,检查其是否已经超时
    else {
        long oldValue = Long.valueOf(jedis.get(lock));
 
        //超时
        if (oldValue < System.currentTimeMillis()) {
            String getValue = jedis.getSet(lock, String.valueOf(value));              
            // 获取锁成功
            if (Long.valueOf(getValue) == oldValue)
                success = true;
            // 已被其他进程捷足先登了
            else
                success = false;
        }
        //未超时,则直接返回失败
        else            
            success = false;
    }        
    pool.returnResource(jedis);
    return success;      
}
 
 
参考:
http://www.cnblogs.com/jianwei-dai/p/6137896.html

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326265663&siteId=291194637