Mastering Distributed Locks with Redis: From Simple SETNX to RedLock and Redisson
This article explains how to implement distributed locks using Redis, starting with a basic SETNX approach, identifying its shortcomings, and then presenting robust solutions such as identifier‑based unlocking, Lua scripts for atomicity, the RedLock algorithm, and Redisson’s advanced features including a watchdog mechanism.
1. Overview
In multithreaded Java programs, synchronized and ReentrantLock guarantee exclusive access within a single JVM. In distributed systems, a similar guarantee requires a distributed lock, which ensures that a shared resource is accessed by only one application instance at a time.
2. Naïve Redis Implementation
Redis, being a shared in‑memory store with high read/write performance, is often used to build a simple distributed lock. The SET command with the NX option inserts a key only when it does not exist, enabling the following workflow:
If the key does not exist, the insertion succeeds and the lock is considered acquired.
If the key already exists, the insertion fails and the lock acquisition fails.
Unlocking is performed by deleting the key.
An expiration time must be set to avoid deadlocks.
// Try to acquire lock
if (setnx(key, 1) == 1) {
// lock acquired, set expiration
expire(key, 30);
try {
// TODO: business logic
} finally {
// release lock
del(key);
}
}This simple approach suffers from several critical problems.
3. Problems with the Naïve Approach
Non‑atomic operations: Multiple commands (SETNX + EXPIRE) are not atomic, leading to possible deadlocks.
Lock‑release errors: If a thread holding the lock blocks and its TTL expires, another thread may acquire the lock and the original thread could mistakenly delete the new lock.
Business‑timeout unlocks: Automatic expiration may cause concurrency issues when the business logic exceeds the TTL.
No re‑entrancy: The lock cannot be re‑entered by the same thread.
3.1 Mis‑deletion Scenario
Thread 1 acquires the lock, blocks, and its TTL expires. Thread 2 then acquires the lock. When Thread 1 resumes, it attempts to delete the key, inadvertently removing Thread 2’s lock.
3.2 Solution: Identifier‑Based Unlocking
Store a unique thread identifier in the lock value. When releasing, compare the stored identifier with the current thread’s identifier; delete only if they match. This also enables re‑entrancy by incrementing a counter.
// Try to acquire lock with identifier
if (setnx(key, "thread-id") == 1) {
expire(key, 30);
try {
// business logic
} finally {
if ("thread-id".equals(get(key))) {
del(key);
}
}
}3.3 Ensuring Atomicity with Lua
Redis does not provide a single atomic API for the above steps, but a Lua script can combine them:
-- Acquire lock with expiration
if (redis.call('setnx', KEYS[1], ARGV[1]) < 1) then
return 0;
end
redis.call('expire', KEYS[1], tonumber(ARGV[2]));
return 1;
-- Release lock only if identifier matches
if (redis.call('get', KEYS[1]) == ARGV[1]) then
return redis.call('del', KEYS[1]);
end
return 0;Calling this script from Java guarantees that lock acquisition and release are atomic.
4. RedLock Algorithm
To improve reliability in a cluster, Redis proposes the RedLock algorithm, which uses multiple independent Redis nodes (recommended at least five). A client attempts to acquire the same lock on each node; if it succeeds on a majority (N/2 + 1) and the total acquisition time is less than the lock’s TTL, the lock is considered acquired.
Record the current Unix time in milliseconds.
Try to set the same key with a unique value (e.g., UUID) on each of the five nodes, using a short network timeout.
Calculate the elapsed time (now – recorded time).
If a majority of nodes granted the lock and elapsed time < TTL, the lock is successful.
Adjust the effective TTL by subtracting the elapsed time.
If acquisition fails, unlock on all nodes to avoid stale locks.
Thus, RedLock tolerates node failures while preventing split‑brain lock acquisition.
5. Redisson – A High‑Level Java Client
Redisson provides a ready‑made implementation of distributed locks, including support for RedLock, re‑entrancy, automatic retries, and a watchdog mechanism. Example usage:
RLock lock = redissonClient.getLock("myLock");
try {
boolean locked = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (locked) {
System.out.println("Lock acquired, executing business logic...");
} else {
System.out.println("Failed to acquire lock.");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
redissonClient.shutdown();
}Redisson stores lock information as a hash {threadId → reentryCount}, enabling true re‑entrancy.
5.1 Watchdog Mechanism
When a lock is held, Redisson starts a background watchdog thread that periodically extends the lock’s expiration (default every 30 seconds). This prevents accidental lock release if the business logic runs longer than the original TTL, while also limiting CPU usage by dynamically adjusting the check interval.
6. Advantages and Disadvantages of Redis‑Based Locks
Pros: High performance, easy to implement with SETNX, avoids single‑point failure when deployed in a cluster.
Cons: Choosing an appropriate TTL is difficult; asynchronous master‑slave replication can cause lock loss during failover; network partitions may lead to split‑brain scenarios where multiple masters grant the same lock.
7. Cluster‑Related Issues
In a master‑slave setup, if a lock is set on the master but not yet replicated, a master failure can cause the lock to disappear. Network partitions (brain split) can create two masters, allowing two clients to hold the same lock simultaneously.
8. Summary
Lock acquisition and release must be performed atomically (e.g., via Lua scripts).
Each lock key should have an expiration to avoid deadlocks.
The lock value must uniquely identify the client to prevent accidental releases.
By combining identifier‑based unlocking, atomic Lua scripts, the RedLock algorithm, or using a mature client like Redisson, developers can build reliable distributed locks on top of Redis.
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.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
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.
