How to use Redis to implement distributed locks

02-11-2023

This article mainly introduces the relevant knowledge of how to implement distributed locks using Redis. The content is detailed and easy to understand, the operation is simple and fast, and has certain reference value. What we have gained, let's take a look at it together.


1. What is a distributed lock

When we write multi-threaded code, different threads may compete for resources, in order to avoid For errors caused by resource contention, we will lock the resource, and only the thread that acquires the lock can continue to execute.

A lock in a process is essentially a variable in memory. When a thread executes an operation to apply for a lock, if it can successfully set the value of the variable representing the lock to 1, it means that the lock has been obtained. Other threads will block when they want to acquire the lock, and the thread that owns the lock will set the value of the lock to 0 after the thread that owns the lock completes the operation, which means that the lock is released.

12_1.jpg

What we are talking about above is the lock between different threads in the process of a server. This lock is placed in memory, and for distributed applications , different applications (processes or threads) are deployed on different servers, so that locks cannot be represented by variables in memory.

Since locks can be expressed on a server through the shared space of memory, then for distributed applications, the storage system can be shared to store a shared lock, which is a distributed lock , and Redis, as a memory database, executes very fast, and is very suitable as a shared storage system for implementing distributed locks.

2. Use Redis to implement distributed lock

For a lock, there are actually only two operations, lock and release lock, let’s see below Let's see how to achieve it through Redis?

2.1 Lock

RedisThe setnx command will determine whether the key exists, if If it exists, do nothing and return 0, if it does not exist, create and assign a value, and return 1, so we can execute setnx to set a value for a representative lock key, if it can be set successfully, then Indicates that the lock is obtained, and the lock cannot be obtained if it fails.

# Use key as lock to represent a lock setnx lock 1

2.2 Release the lock

After performing the operation, when you want to release the lock, directly set the Redis Just delete the key value lock, so that other processes can pass thesetnx command resets and acquires the lock.

# Release the lock del lock

Through the above two commands, we have implemented a simple distributed lock, but there is a problem here: if a process is locked by the setnx command, When a specific operation goes wrong, there is no way to release the lock in time, then other processes cannot obtain the lock, and the system cannot continue to execute. The solution to this problem is to set a validity period for the lock. After the validity period, the lock is automatically released .

2.3 Set the validity period for the lock

It is very simple to set the validity period for the lock, directly use expire of Redis code> command is fine, such as:

# lock setnx lock 1 # Set a 10s validity period for the lock expire lock 10

However, there is another problem now. If we set the lock and execute the expire command before the process hangs up, then expire is not executed successfully, and the lock is not released, so we must ensure that the above two commands are executed together, how to guarantee it?

There are two methods, one is to use the script written in LUA language, and the other is to use the set command of Redis , after the set command is followed by the nx parameter, the execution effect is consistent with setnx, and the set command can be followed by < code>ex parameter to set the expiration time, so we can use the set command to combine setnx and expire together, In this way, the atomicity of execution can be guaranteed.

# Determine whether the key value exists, ex is followed by the validity period of the key value, 10s set lock 1 nx ex 10

Solved the effective problem of the lock, now let's look at another problem.

not_adv_1.jpg

As shown in the picture above, there are now three different A, B, C A process on the server needs to acquire a lock when performing an operation, and release the lock after execution.

The current situation is process A got stuck when executing step 2 (shown in the green area above), and the time has exceeded the lock validity period, so the lock set by process A is automatically released. At this time, process B< /code> obtained the lock and started to execute the operation, but because process A is just stuck, so when it will continue to execute, the lock will be released manually in step 3, but at this time , the lock is owned by thread B, that is to say, what process A deletes is not its own lock, but the lock of process B, at this time process BNot finished yet, but after the lock is released, Process C can lock it, That is to say, because process A freezes and releases the wrong lock, process B and process C can acquire it at the same time lock.

How to avoid this situation? How to distinguish the locks of other processes and avoid deleting the locks of other processes? The answer is that when each process locks, it sets a unique value for the lock, and when releasing the lock, it judges whether it is a lock set by itself.

2.4 Set a unique value for the lock

When setting a unique value for the lock, the same is to use the set command, the only The difference is to change the key value 1 to a randomly generated unique value, such as uuid.

# rand_uid means unique id set lock rand_id nx ex 10

When the value in the lock is set by the process and the lock is released, you need to judge whether the lock is your own. The steps are as follows:

  • Get the value of the lock through the get command of Redis

  • According to the obtained value, determine whether the lock is your own If it is set

  • , release the lock through the del command.

At this point, we can see that three operations need to be performed to release the lock. If the three operations are performed in sequence, there is no way to guarantee atomicity, such as the process A is about to execute the del command after executing step 2, but the lock expires and is automatically released, and is used by process B on other servers Get the lock, but at this time thread A executes del or deletes the lock of thread B.

The solution to this problem is to ensure the execution of the above three operationsAtomicity, that is, during the three operations of releasing the lock, other processes cannot acquire the lock. To do this, you need to use LUA scripts.

2.5 Realize the atomicity of releasing locks through LUA scripts

Redis supports LUA scripts, When the code in LUA is executed, the requests of other clients will not be executed, which can ensure atomic operation, so we can use the following script to release the lock:

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

After saving the above script as a script, you can call the Redis client command redis-cli to execute, as follows:

# lock is the key, and rand_id represents the value saved in the key redis-cli --eval unlock.lua lock , rand_id


Copyright Description:No reproduction without permission。

Knowledge sharing community for developers。

Let more developers benefit from it。

Help developers share knowledge through the Internet。

Follow us

Recommended reading

high perspicacity