Mastering Redis Distributed Locks: From Basics to Advanced Redlock Strategies
This comprehensive guide walks through the fundamentals, pitfalls, and advanced implementations of Redis distributed locks—including basic SETNX usage, safe lock release with Lua scripts, re‑entrant lock design, timeout handling, Redlock debates, and practical Redisson integration—providing Java developers with actionable patterns for reliable concurrency control.
Distributed Lock Introduction
Redis distributed locks control concurrent access to a shared resource so that only one JVM thread can hold the lock at any given time.
When do we need a distributed lock?
When to acquire a lock?
Where to place lock/unlock code?
How to avoid a lock that can never be released?
What is a suitable timeout?
How to prevent other threads from releasing the lock?
How to implement a re‑entrant lock?
What safety issues arise in master‑slave architectures?
What is Redlock?
Best practices for Redisson distributed locks
Watchdog implementation principles
Basic Concepts
A lock ensures that when many clients read/write a shared resource, only one thread accesses it at a time.
Distributed lock = allowing only one JVM thread to access the protected resource at a time.
Lock Features
Mutual exclusion: only one client can hold the lock at any moment.
No deadlock: a client can always acquire the lock even if another client crashes.
Fault tolerance: as long as a majority of Redis nodes are up, the client can acquire and release the lock.
Can we use SETNX key value to achieve the mutual‑exclusion feature?
SETNX(SET if Not eXists) returns 1 if the key was set, 0 otherwise.
> SETNX lock:168 1
(integer) 1 # lock acquiredIf another client tries to acquire the same lock:
> SETNX lock:168 2
(integer) 0 # lock acquisition failedAfter the business logic finishes, the lock must be released with DEL:
> DEL lock:168
(integer) 1What if the lock cannot be released?
Two scenarios can cause a lock to remain forever:
The client node crashes before releasing the lock.
An exception prevents the execution of the DEL command.
Timeout Settings
Set an expiration when acquiring the lock, e.g., 60 seconds for a massage service:
> SETNX lock:168 1 # acquire lock
(integer) 1
> EXPIRE lock:168 60 # auto‑delete after 60 s
(integer) 1However, setting timeout and lock acquisition as two separate commands is not atomic; the timeout might fail, leaving the lock without expiration.
Redis 2.6+ supports an atomic SET with NX and PX options:
SET resource_name random_value NX PX 30000To ensure only the owner can release the lock, store a unique identifier as the value and compare it before deleting.
// Pseudocode
if (redis.get("lock:168").equals(uuid)) {
redis.del("lock:168");
}Because GET+DEL is not atomic, a Lua script is used:
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
endCorrect Lock/Unlock Placement
public void doSomething() {
try {
redisLock.lock(); // acquire
// business logic
} catch (Exception e) {
e.printStackTrace();
} finally {
redisLock.unlock(); // always release
}
}Lock acquisition should be inside the try block so that any exception after the lock command still guarantees the finally block runs.
Re‑entrant Lock Implementation
Re‑entrancy allows the same thread to acquire the lock multiple times, incrementing a counter each time and decrementing it on unlock. When the counter reaches zero, the lock is truly released.
Redisson implements this using a Redis hash where the field is the unique client identifier and the value is the lock count.
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('hincrby', KEYS[1], ARGV[2], 1)
redis.call('pexpire', KEYS[1], ARGV[1])
return 1
end
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
redis.call('hincrby', KEYS[1], ARGV[2], 1)
redis.call('pexpire', KEYS[1], ARGV[1])
return 1
end
return 0Unlocking decrements the counter and deletes the hash when it reaches zero:
if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then
return nil
end
local counter = redis.call('hincrby', KEYS[1], ARGV[1], -1)
if (counter > 0) then
return 0
else
redis.call('del', KEYS[1])
return 1
endMaster‑Slave Issues and Redlock
In asynchronous replication, a client may acquire a lock on the master, the master crashes before replication, and a slave becomes the new master, allowing another client to acquire the same lock—breaking mutual exclusion.
Redlock proposes using N independent Redis masters (commonly 5) and requiring a majority (N/2+1) to grant the lock within a time window shorter than the lock’s TTL.
Critics argue Redlock’s assumptions about synchronized clocks and network latency are unrealistic, and that consensus systems like Zookeeper provide stronger safety guarantees.
Redisson Practical Usage
Add the Maven dependency:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.16.4</version>
</dependency>Configure Spring Boot Redis properties (cluster or sentinel as needed).
Obtain a lock:
RLock lock = redisson.getLock("myLock");
try {
lock.lock(); // blocks until acquired, watchdog auto‑renews
// business logic
} finally {
lock.unlock();
}Variants:
Try lock with wait time: lock.tryLock(10, TimeUnit.SECONDS) Lock with lease time (no watchdog): lock.lock(10, TimeUnit.SECONDS) Try lock with lease and wait time: lock.tryLock(100, 10, TimeUnit.SECONDS) The watchdog renews the lock every lockWatchdogTimeout/3 milliseconds (default 30 s timeout, renewal every 10 s) as long as no explicit lease time is set.
private void scheduleExpirationRenewal(long threadId) {
// schedule a timer that runs every internalLockLeaseTime/3
// calls a Lua script to pexpire the lock if it still exists
}Key points:
Place unlock in a finally block.
Watchdog works only when lease time is not explicitly set.
Do not set lockWatchdogTimeout too low; otherwise the lock may expire before renewal.
Conclusion
The article walks through the entire lifecycle of Redis distributed locks, from simple SETNX usage to sophisticated Redisson integration, highlighting common pitfalls, re‑entrancy, timeout strategies, master‑slave challenges, and the Redlock debate, equipping developers with a solid foundation for safe concurrency control.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Bin's Tech Cabin
Original articles dissecting source code and sharing personal tech insights. A modest space for serious discussion, free from noise and bureaucracy.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
