Before the speech
When it comes to distributed applications, it is necessary to leave the problem of distributed lock 🔐. Distributed lock is widely used in distributed applications. This article will talk about some problems of distributed lock based on Redis.
The lock
Coder may be exposed to the most in the multi-threaded environment, in order to ensure that a code block can only be accessed by one thread at the same time, such as the following figure:
For single-process concurrent scenarios, you can use locks provided by programming languages and libraries, such as synchronized syntax in Java and ReentrantLock, mutex under the Sync package in Golang, Async_std ::sync::Mutex in Rust, to avoid concurrency problems, is actually a way to lock locally.
A distributed lock
But now popular distributed architecture, in a distributed environment, how to ensure that the threads of different nodes execute synchronously? Or how to lock shared resources??
Single before will be broken down into a distributed application system, for some complicated scene button while reading public resources such as inventory, such as selling tickets demand can simply use synchronous or lock can be achieved, but after application of the distributed system changed from the previous single processes multi-threaded programs to the multi-process multi-threaded, At this point, using the above solution is obviously not enough.
The general industry has several solutions:
- Based on the
DB
Unique index of - Based on the
Memcached
theThe add command
- Based on the
Zookeeper
Temporary ordered node of - Based on the
Redis
的NX EX
- Based on the
Chubby
Coarse-grained distributed lock services
Redis
How many holes did you fill in?
If in the distributed scene, realize the synchronization access of different client threads to code and resources, ensure the security of processing shared data in multi-threading, you need to use distributed lock technology, I will write some pits based on Redis 😁.
In distributed mode, when modifying existing data in the program, it is necessary to read the data first and then save the modification. In this case, it is easy to encounter concurrency problems. Because modification and saving are not atomic operations, some operations on data may be lost in concurrent scenarios, and local locks cannot take effect between multiple servers. In this case, distributed locks are required to ensure data consistency.
Redis lock is mainly implemented by Redis setnx command.
- Lock command:
SETNX key value
If the key does not exist, set the key and return success; otherwise, return failure.KEY
It is the unique identifier of a lock and is named based on the service. - Unlock command:
DEL key
Release the lock by removing key/value pairs so that other threads can pass throughSETNX
Command to obtain the lock. - The lock timeout:
EXPIRE key timeout
To set upkey
To ensure that even if the lock is not explicitly released, the lock can be automatically released after a certain period of time to avoid the resource being locked forever.
// Pseudo-code implementation
fn main() {
let key: &'static str = "sync_lock";
if up_lock(key, 1) = =1 {
// Set timeout
expire(key, 30)
/ /... The business logic}}// Implementation based on Redis SETNX and EXPIRE, problem code
fn up_lock(key: &'static str, num: i8) - >i8 {
/ /... Lock your logic
return 1;
}
fn expire(key: &'static str, num: i8) {
/ /... Custom timeout
}
Copy the code
Finish writing so a look still have no what problem, in fact above 🕳 pit big!! If this is the way you do it, congratulations, you are in a hole 😜 (PS: this is not a code problem, but SETNX and EXPIRE non-atomic).
SETNX
和EXPIRE
The atomic
If SETNX succeeds, the server hangs, restarts, or suffers network problems after the EXPIRE command is set, causing the lock to become a deadlock without the EXPIRE timeout set.
- Lock in addition to the misunderstanding
If thread A successfully obtains the lock and the expiration time is set to 30 seconds, but thread A’s execution time exceeds 30 seconds, the lock expiration is automatically released, and thread B has obtained the lock. Thread A uses the DEL command to release the lock, but the lock added by thread B has not been completed yet. The actual lock released by thread A is the lock added by thread B, resulting in confusion of the lock, confusion of the actual logical code and even loss of key data.
- Timeout unlock causes concurrency
If thread A successfully obtains the lock and sets the expiration time of 30 seconds, but thread A’s execution time exceeds 30 seconds, the lock expiration will be released automatically. At this time, thread B obtains the lock, thread A and thread B execute concurrently, then there is no significance of the existence of distributed lock 🤷.
- Set the expiration time long enough to ensure that the code logic can complete before the lock is released.
- Add daemons to the thread that acquires the lock, and add a lifetime to the lock that is about to expire but has not been released.
- non-reentrant
If a thread can hold a lock more than once, the lock is reentrant. If a non-reentrant lock is relocked, the relock will fail because the lock is already held. Redis can re-count locks by incrementing them by one, subtracting them by one, and releasing them when the count returns to zero.
The small knot
Redis is a high-performance middleware, but there are still problems if used in distributed lock implementation, I see a lot of network articles are mostly using Redis to solve distributed lock problems, I hope this article can help you, remember to point attention!!