Mastering Redis Distributed Locks: From Basics to Robust Java Implementations
This article explains how to build reliable distributed locks with Redis, covering essential commands, common pitfalls, improvements using GETSET, Lua scripting for atomicity, and complete Java code examples that address deadlock and unlock vulnerabilities.
Related Redis Commands
Key Redis commands used for locking include SETNX (set if not exists), EXPIRE (set key expiration), GETSET (set new value and return old value), and the scripting commands EVAL / EVALSHA introduced in Redis 2.6.
Distributed Lock Basics
Using the atomicity of SETNX together with EXPIRE a simple non‑blocking lock can be created. The basic algorithm is shown in the following pseudocode:
boolean tryLock(String key, int lockSeconds) {
if (SETNX key "1" == 1) {
EXPIRE key lockSeconds;
return true;
} else {
return false;
}
}
boolean unlock(String key) {
DEL key;
}The method returns immediately when the lock cannot be obtained; a blocking version can be built by repeatedly calling tryLock with a sleep interval. However, if the client crashes between the SETNX and EXPIRE calls, the lock may never expire, causing a permanent deadlock.
Improving the Lock
To avoid the above race condition, the lock value can store the absolute expiration timestamp (current time + lock time). When acquiring the lock fails, the client reads the stored timestamp and, if it is already past, deletes the stale key and retries. This still suffers from a race condition when two clients perform DEL followed by SETNX simultaneously:
C1 DEL key
C1 SETNX key <expireTime>
C2 DEL key
C2 SETNX key <expireTime>Both clients may think they hold the lock. Using GETSET solves this: the client sets a new expiration time and compares the returned old value; a mismatch indicates the lock was taken by another client.
A complete Java implementation that incorporates these ideas is provided, including methods for non‑blocking lock, polling lock, and unlocking, as well as utility functions to convert between long and byte[].
Optimizing the Lock
The previous approach requires at least two round‑trips to Redis for each lock attempt, which hurts performance and still depends on synchronized clocks across servers. Redis 2.6+ allows executing a Lua script atomically, eliminating the gap between SETNX and EXPIRE.
The script used is:
if (redis.call('setnx', KEYS[1], ARGV[1]) == 1) then
redis.call('expire', KEYS[1], tonumber(ARGV[2]))
return true
else
return false
endAlthough the script itself is not strictly atomic if EXPIRE fails, such a failure usually indicates a server crash, which is outside the scope of client‑side safety. The final Java class wraps this script in a RedisScript object and provides the same lock API with better performance and reliability.
Conclusion
The author notes that the presented code is a personal learning experiment and invites readers to point out any remaining bugs.
Unlock Bug (Update)
A serious unlock issue is identified: the simple DEL key can mistakenly delete another client’s lock if the original holder’s work exceeds the lock’s expiration time. The fix stores a unique lockValue (UUID plus timestamp) when the lock is created and, before unlocking, checks that the stored value matches.
The atomic check‑and‑delete is performed with another Lua script:
if (redis.call('get', KEYS[1]) == ARGV[1]) then
redis.call('del', KEYS[1])
return true
else
return false
endThe updated Java implementation incorporates this script, ensures the lock is only released by its owner, and adds safeguards against double unlocking.
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.
21CTO
21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.
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.
