This article will introduce you to distributed locks in Redis, why distributed locks are needed, and how Redis implements distributed locks. I hope it will be helpful to you!
Why distributed locks are needed
Use The purpose of distributed locks is nothing more than to ensure that only one client can operate on shared resources at the same time.
We often encounter concurrency problems when performing logical processing in distributed applications. [Related recommendations: Redis Video Tutorial]
For example, an operation requires modifying the user's status. To modify the status, you need to read the user's status first, modify it in the memory, and then save it back after the modification. . If such operations are performed at the same time, concurrency problems will arise because the two operations of reading and saving state are not atomic.
At this time, distributed locks are used to limit the concurrent execution of the program. As a caching middleware system, redis can provide this kind of distributed lock mechanism.
The essence is to occupy a pit in redis. When other processes want to occupy the pit, they find that they have already occupied the pit. If it is occupied, just wait and try again later
Generally speaking, the distributed locks available in the production environment need to meet the following points:
innodb_lock_wait_timeout
configuration in the MySQL InnoDB engine to prevent unnecessary thread waiting and resource waste through timeout release; Use SETNX to implement
The usage method of SETNX is: SETNX key value
. Only when the key key does not exist, the value of the key key is set to value. If the key key exists, SETNX does not take any action. .
boolean result = jedis.setnx("lock-key",true)== 1L; if (result) { try { // do something } finally { jedis.del("lock-key"); } }
This solution has a fatal problem, that is, after a thread acquires the lock and cannot perform the unlocking operation normally due to some abnormal factors (such as downtime), then the lock will never be released. .
To this end, we can add a timeout period to this lock
The effect of executing SET key value EX seconds
is equivalent to executing SETEX key seconds value
The effect of executing SET key value PX milliseconds
is equivalent to executing PSETEX key milliseconds value
String result = jedis.set("lock-key",true, 5); if ("OK".equals(result)) { try { // do something } finally { jedis.del("lock-key"); } }
The solution looks perfect , but in fact there will still be problems
Just imagine that a certain thread A acquires the lock and sets the expiration time to 10s, and then it takes 15s to execute the business logic. At this time, thread A acquires The lock has already been automatically released by the expiration mechanism of Redis
After thread A acquires the lock and 10 seconds have passed, the changed lock may have been acquired by other threads. When thread A finishes executing the business logic and prepares to unlock (DEL key
), it is possible to delete the lock that has been acquired by other threads.
So the best way is to determine whether the lock belongs to you when unlocking. We can set the value to a unique value when setting key
uniqueValue
( It can be a random value, UUID, or a combination of machine number and thread number, signature, etc.).
When unlocking, that is, when deleting the key, first determine whether the value corresponding to the key is equal to the previously set value. If it is equal, the key can be deleted
String velue= String.valueOf(System.currentTimeMillis()) String result = jedis.set("lock-key",velue, 5); if ("OK".equals(result)) { try { // do something } finally { //非原子操作 if(jedis.get("lock-key")==value){ jedis.del("lock-key"); } } }
We can see the problem at a glance here Come on: GET
and DEL
are two separate operations. An exception may occur in the gap between GET execution and before DEL execution.
If we only need to ensure that the unlocking code is atomic we can solve the problem
Here we introduce a new method, which is Lua script, the example is as follows :
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
where ARGV[1]
represents the unique value specified when setting the key.
Due to the atomicity of the Lua script, during the process of Redis executing the script, other client commands need to wait for the Lua script to be executed before they can be executed.
Ensure that the expiration time is greater than the business execution time
In order to prevent multiple threads from executing business code at the same time, it is necessary to ensure that the expiration time is greater than the business execution time
Add A boolean type attribute isOpenExpirationRenewal
, used to identify whether to enable scheduled refresh expiration time
In addition, a scheduleExpirationRenewal
method is used to enable the thread to refresh the expiration time
The locking code sets isOpenExpirationRenewal to true after successfully acquiring the lock, and calls the scheduleExpirationRenewal
method to start the thread that refreshes the expiration time.
The unlocking code adds a line of code and sets the isOpenExpirationRenewal attribute. If false, stop the thread polling for refreshing the expiration time
Redisson implementation
If the lock is successfully acquired, a scheduled task will be started, and the scheduled task will be checked regularly for renewal
The time difference between each call of this scheduled schedule is internalLockLeaseTime / 3
, which is 10 seconds
By default, the locking time is 30 seconds. If the locked business does not After execution, when 30-10 = 20
seconds, a renewal will be performed and the lock will be reset to 30 seconds
In the cluster, when the master node hangs up, the slave node will take its place, but there is no obvious perception on the client. It turns out that the first client successfully applied for a lock on the master node, but before the lock could be synchronized to the slave node, the master node suddenly died. Then the slave node becomes the master node. This new node does not have this lock inside, so when another client comes to request a lock, it is approved immediately. This will cause the same lock in the system to be held by two clients at the same time, resulting in insecurity
The Redlock algorithm is to solve this problem
To use Redlock, you need to provide multiple Redis
instances. These instances were previously independent of each other and had no master-slave relationship. Like many distributed algorithms, redlock also uses most mechanisms
When locking, it will send set instructions to more than half of the nodes. As long as more than half of the nodesset
succeed, the lock is considered successful. . When releasing the lock, a del instruction needs to be sent to all nodes. However, the Redlock algorithm also needs to consider many detailed issues such as error retry and clock drift. At the same time, because Redlock
needs to read and write to multiple nodes, it means that the performance of Redis will be lower than that of a single instance.
Redlock algorithm is a high-availability mode introduced on the basis of a single Redis node. Redlock is based on N completely independent Redis nodes, usually an odd number greater than 3 (usually N can be set to 5), which can basically ensure that the cluster Each node will not go down at the same time.
Assuming that the current cluster has 5 nodes, the client running the Redlock algorithm performs the following steps in order to complete the operation of acquiring the lock
In other words, assuming that the lock expires in 30 seconds and it takes 31 seconds to lock the three nodes, naturally the locking failed
In the Java client officially recommended by RedisRedisson
has a built-in implementation of RedLock
https://redis.io/topics/distlock
https:// github.com/redisson/redisson/wiki
RedLock problem:
RedLock only ensures the high availability of the lock, but does not guarantee the correctness of the lock
RedLock is a distributed system that relies heavily on the system clock
Martin’s criticism of RedLock:
This article is reproduced from: https://juejin.cn/post/7018968452136173576
Author: With a distant heart
More programming related For knowledge, please visit: programming video! !
The above is the detailed content of Why is distributed lock needed in Redis? How to achieve?. For more information, please follow other related articles on the PHP Chinese website!