"Redis" redis distributed lock case explanation

Ginger ginger 2022-01-26 15:45:42 阅读数:795

redis redis distributed lock case

Redis Distributed locking case

Related video tutorials ( From the power node ):https://www.bilibili.com/video/BV1Uz4y1X72A 

Download relevant information :http://www.bjpowernode.com/?cnblogs

1. Component dependency

First we have to go through Maven introduce Jedis Open source components , stay pom.xml Add the following code to the file :

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>

2. Lock code

Show the code first , Then I'll take you to explain why this is achieved :

public class RedisTool {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
/**
* Try to get distributed lock
* @param jedis Redis client
* @param lockKey lock
* @param requestId The request id
* @param expireTime Beyond the time
* @return Success or failure
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
}

You can see , We lock on a line of code :jedis.set(String key, String value, String nxxx, String expx, int time), This set() Method has five parameters :

  • The first is key, We use key To be a lock , because key Is the only one. .
  • The second is value, What we pass is requestId, Many children's shoes may not understand , Yes key As a lock is not enough , Why use value? The reason is that when we talk about reliability above , In order to solve the fourth condition of the distributed lock, the ringer is needed , By giving value The assignment is requestId, We will know which request this lock was added , When unlocking, it can be based on .requestId have access to UUID.randomUUID().toString() Method generation .
  • The third is nxxx, This parameter we fill in is NX, intend SET IF NOT EXIST, When key When there is no , We carry out set operation ; if key Already exist , Then do nothing ;
  • The fourth is expx, This parameter we pass is PX, It means we're going to give this key Add an expired setting , The specific time is determined by the fifth parameter .
  • The fifth is time, Corresponding to the fourth parameter , representative key The expiration time of .

in general , Execute the above set() Methods lead to only two results :1. No current locks (key non-existent ), Then do the lock operation , And set an expiration date for the lock , meanwhile value Represents the locked client .2. Existing lock , Do nothing .

The children's shoes with a thin heart will find , Our lock code satisfies three conditions described in our reliability . First ,set() Joined the NX Parameters , You can guarantee that if you have key There is , The function will not be called successfully , That is, only one client can hold the lock , Satisfy the mutual exclusion . secondly , Because we set the expiration time for the lock , Even if the lock holder subsequently crashes without unlocking , The lock will also be automatically unlocked when the expiration time is reached ( namely key Be deleted ), A deadlock will not occur . Last , Because we're gonna value The assignment is requestId, Represents the client request ID for locking , Then when the client is unlocked, it can check whether it is the same client . Because we only consider Redis Scenario of stand-alone deployment , So we don't think about fault tolerance .

3. The wrong sample 1

A common example of error is the use of jedis.setnx() and jedis.expire() Combination implementation lock , The code is as follows :

public static void wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) {
Long result = jedis.setnx(lockKey, requestId);
if (result == 1) {
// If the program crashes suddenly here , The expiration time cannot be set , A deadlock will occur 

jedis.expire(lockKey, expireTime);
}
}

setnx() What the method does is SET IF NOT EXIST,expire() The way is to add an expiration time to the lock . At first glance it looks like the one in front set() The method gives the same result , But because these are two Redis command , It's not atomic , If the program is running out setnx() And then it collapsed. , Causes the lock to have no expiration time set . Then there will be a deadlock . The reason why someone on the Internet has realized this , Because of the lower version of jedis Multi parameter is not supported set() Method .

4. The wrong sample 2

This kind of error example is more difficult to find problems , And the implementation is more complex . Realize the idea : Use jedis.setnx() Command implementation lock , among key It's a lock ,value It's the expiration time of the lock .

Execution process :1. adopt setnx() Method to try locking , If the current lock does not exist , Returns lock successfully .2. If the lock already exists, get the expiration time of the lock , Compare it to the current time , If the lock has expired , Sets a new expiration time , Returns lock successfully .

The code is as follows :

public static boolean wrongGetLock2(Jedis jedis, String lockKey, int expireTime) {
long expires = System.currentTimeMillis() + expireTime;
String expiresStr = String.valueOf(expires);
// If the current lock does not exist , Returns lock successfully 
if (jedis.setnx(lockKey, expiresStr) == 1) {
return true;
}
// If the lock exists , The expiration time of the lock acquired 

String currentValueStr = jedis.get(lockKey);
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
// Lock expired , Gets the expiration time of the last lock , And set the expiration time of the current lock 

String oldValueStr = jedis.getSet(lockKey, expiresStr);
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
// Consider the case of multi-threaded concurrency , Only one thread has the same set value as the current value , It has the right to lock it 
return true;
}
}
// Other situations , All return locks failed 
return false;
}

So what's the problem with this code ?

1、 Due to the client's own generation expiration time , So we need to force the time synchronization of each client under the distributed environment .

2、 When the lock expires , If multiple clients execute at the same time jedis.getSet() Method , So, although only one client can lock in the end , However, the expiration time of this client's lock may be overridden by other clients .

3、 Locks do not have owner identity , That is, any client can unlock .

copyright:author[Ginger ginger],Please bring the original link to reprint, thank you. https://en.javamana.com/2022/01/202201261545410762.html