Several implementations of redis locking

1. Redis lock classification

  1. redis use of the points table is a lock command INCR, SETNX,SET

2. The first lock commandINCR

The idea of ​​this locking is that if the key does not exist, the value of the key will be initialized to 0 first, and then the INCR operation will be executed to increment by one. 
Then when other users perform the INCR operation and increase by one, if the returned number is greater than 1, it means that the lock is being used.

1、 客户端A请求服务器获取key的值为1表示获取了锁

2、 客户端B也去请求服务器获取key的值为2表示获取锁失败

3、 客户端A执行代码完成,删除锁

4、 客户端B在等待一段时间后在去请求的时候获取key的值为1表示获取锁成功

5、 客户端B执行代码完成,删除锁


$redis->incr($key);

$redis->expire($key, $ttl); //设置生成时间为1秒

 

3. The second lockSETNX

The idea of ​​locking is that if the key does not exist, set the key to value. 
If the key already exists,  SETNX no action will be taken.

 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功

 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败

 客户端A执行代码完成,删除锁

 客户端B在等待一段时间后在去请求设置key的值,设置成功

 客户端B执行代码完成,删除锁


$redis->setNX($key, $value);

$redis->expire($key, $ttl);

4. The third lock SET

There is a problem with the above two methods. You will find that you need to set the key to expire. So why set key expiration? If the request execution exits unexpectedly for some reason, causing the lock to be created but not deleted, the lock will always exist, so that the cache will never be updated in the future. So we need to add an expiration time to the lock to prevent accidents. 
However, setting with Expire is not an atomic operation. So you can also use transactions to ensure atomicity , but there are still some problems, so the official quoted another one. The use of the SET command itself has included the function of setting the expiration time from version 2.6.12.

1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功

2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败

3、 客户端A执行代码完成,删除锁

4、 客户端B在等待一段时间后在去请求设置key的值,设置成功

5、 客户端B执行代码完成,删除锁


$redis->set($key, $value, array('nx', 'ex' => $ttl)); //ex表示秒

5. Other issues

Although the above step has met our needs, but still need to consider other issues? 
1. What should I do if redis finds that the lock fails? Interrupt request or cyclic request? 
2. In the case of circular request, if one acquires the lock, is it possible to grab the lock when the other acquires the lock? 
3. After the lock expires in advance, client A has not finished executing, and then client B has acquired the lock. At this time, client A has finished executing, will the lock of B be deleted when deleting the lock?

6. Solution

For question 1: Use a loop request, loop request to obtain a lock. For 
question 2: For the second problem, add a sleep function when looping to request a lock, and wait a few milliseconds to execute the loop. For 
problem 3: when locking The key deposited is random. In this case, each time you delete the key, judge whether the value stored in the key is the same as the one stored by yourself

do { //针对问题1,使用循环

$timeout = 10;

$roomid = 10001;

$key = 'room_lock';

$value = 'room_'.$roomid; //分配一个随机的值针对问题3

$isLock = Redis::set($key, $value, 'ex', $timeout, 'nx');//ex 秒

if ($isLock) {

if (Redis::get($key) == $value) { //防止提前过期,误删其它请求创建的锁

//执行内部代码

Redis::del($key);

continue;//执行成功删除key并跳出循环

}

} else {

usleep(5000); //睡眠,降低抢锁频率,缓解redis压力,针对问题2

}

} while(!$isLock)

7. Another lock

The above locks fully meet the needs, but the official also provides a set of locking algorithms, here is PHP as an example

$servers = [

['127.0.0.1', 6379, 0.01],

['127.0.0.1', 6389, 0.01],

['127.0.0.1', 6399, 0.01],

];


$redLock = new RedLock($servers);


//加锁

$lock = $redLock->lock('my_resource_name', 1000);


//删除锁

$redLock->unlock($lock)

The above is an official locking method, which is the same as the sixth method, except that the official one is more robust. So you can directly use the official class method to call. The official provides how to implement locks in various languages.

Guess you like

Origin blog.csdn.net/qq_27828675/article/details/110850631