Distributed | Entering BAT means using Redis to implement distributed locks

Thirteen, Redis implements distributed locks

Why use distributed locks?

In the process of multi-threaded development, we certainly cannot avoid using locks. Jdk also provides a large number of lock functions, but why do we manually develop a distributed lock? The reason is that the locks we use in traditional projects are the same In a process, they can access each other's resource information, but in distributed, each project is running in a different process, they can not share resource information, so they need to be able to between different processes A third party that performs "communication" realizes this function, then redis actually has this function.

Redis realizes the principle of distributed lock

In fact, the principle of redis implementation is mainly that a certain thread now occupies a pit in redis, and then when people behind come in and see that the pit is occupied, they have been waiting for others to release the pit or give up. After release, he will seize it again.

Simple implementation of distributed locks

#Seize a pit, use the setnx instruction, if someone else created it, the setting fails, that is, the corresponding lock acquisition failure
setnx lock:user_yang true#Implement our business logic, after the logic is processed, call the del instruction to release the lock
#If the lock is seized When the thread hangs up, the lock will never be released, which will cause the resource to be occupied all the time, so you can set an expiration time and automatically release the lock at the point
expire lock:user_yang 10
del lock:user_yang

Question: If there is a problem between setting the lock and setting the timeout period, what if the timeout is not set, and the lock cannot be released? Is it as long as the atomic operation is guaranteed?

In order to solve the above problems, in redis2.8, the author added the extended operation of the set command, so that setnx and expire can be executed together to ensure their atomic operation.

Enhanced distributed lock

set lock:user_yang true ex 5 nx
...
del lock:user_yang

Timeout problem

The timeout problem refers to that when the execution time of our business code exceeds the time for setting the lock, the lock release will be occupied by other threads, which will cause the code in the critical section to not be strictly executed, and when the main business code is executed, it will Will release this lock, this will lead to the release of other people's locks, and will cause a series of problems.

  • solution

When setting the lock, a value is randomly inserted, and the value is compared before the lock is deleted. If it is the same as the previously set value, it is deleted, otherwise it is not deleted.

  • Atomicity problem

Because comparison and deletion are not atomic operations, will they cause new problems, but does redis provide such atomic operation instructions?

  • Solution 2

Use LUA script

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

java implements distributed lock

  • Introduce dependencies
 <dependency>
     <groupId>redis.clients</groupId>
     <artifactId>jedis</artifactId>
     <version>2.9.0</version>
 </dependency>
  • Public constant
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";
private static final Long RELEASE_SUCCESS = 1L;
  • Acquire lock
     /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    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;
    }

  • Release lock
    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }
  • Client demo
 @Test
    public void getLock(){
        Jedis jedis = new Jedis("172.16.51.129",6379);
        String requestId = UUID.randomUUID().toString();
        boolean boolSuccess = RedisUtils.tryGetDistributedLock(jedis, "lock:user_yang",requestId, 10);
        if(boolSuccess){
            System.out.println("成功获取锁");
        }else{
            System.out.println("获取锁失败");
        }
        boolSuccess = RedisUtils.tryGetDistributedLock(jedis, "lock:user_yang", requestId, 10);
        if(boolSuccess){
            System.out.println("成功获取锁");
        }else{
            System.out.println("获取锁失败");
        }
        boolSuccess = RedisUtils.releaseDistributedLock(jedis, "lock:user_yang", requestId);
        if(boolSuccess){
            System.out.println("释放锁成功");
        }else{
            System.out.println("释放锁失败");
        }
    }

Guess you like

Origin blog.csdn.net/weixin_34311210/article/details/108290682