Mastering Distributed Locks with Redis: From Basic SETNX to RedLock and Redisson
This article explains how to implement distributed locks using Redis, starting with a simple SETNX approach, identifying its shortcomings, and then presenting solutions such as thread‑identifiers, Lua scripts for atomicity, handling TTL with watchdogs, the RedLock algorithm, and practical usage via the Redisson Java client.
Distributed Lock Basics
In a single JVM, synchronized or ReentrantLock provides mutual exclusion. In a distributed system a lock must work across multiple nodes.
Characteristics of a Distributed Lock
Mutual exclusion : only one client holds the lock at a time.
Reentrancy : the same client/thread can acquire the lock multiple times.
Timeout : the lock expires automatically to avoid deadlock.
High performance & availability : lock and unlock operations should be cheap and resilient.
Blocking / non‑blocking : clients can wait or be notified promptly.
Naïve Redis Implementation
Redis can be used as a shared store. The SET command with the NX option implements “set if not exists”.
If the key does not exist, SETNX succeeds → lock acquired.
If the key exists, SETNX fails → lock not acquired.
Unlock by deleting the key.
Set an expiration time to prevent deadlock.
// Try to acquire lock
if (setnx(key, 1) == 1) {
// Lock acquired, set expiration (seconds)
expire(key, 30);
try {
// business logic
} finally {
// Release lock
del(key);
}
}Problems with this approach:
Non‑atomic operations : separate commands leave a window where the lock may be left without TTL.
Accidental unlock : a client whose TTL expires may delete a lock that another client has already acquired.
Business timeout : automatic expiration can interrupt long‑running tasks.
No reentrancy : the same client cannot reacquire the lock.
Solutions
1. Prevent Accidental Deletion
Store a unique identifier (e.g., UUID or thread ID) as the lock value and delete the key only if the stored value matches.
// Try to acquire lock with identifier
if (setnx(key, "myId") == 1) {
expire(key, 30);
try {
// business logic
} finally {
// Release only if identifier matches
if ("myId".equals(get(key))) {
del(key);
}
}
}This also enables re‑entrancy by incrementing a counter when the same identifier re‑acquires the lock.
2. Ensure Atomicity with Lua
Combine SETNX and EXPIRE in a single Lua script so that a crash cannot leave the lock without TTL.
-- Acquire lock atomically
if redis.call('setnx', KEYS[1], ARGV[1]) == 0 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 03. Automatic Expiration Handling (Watchdog)
If the owning client’s work exceeds the TTL, a background watchdog thread can periodically extend the TTL until the lock is released.
Redis‑Based Lock Pros & Cons
High performance : Redis read/write latency is low.
Simple to implement : SETNX provides a basic primitive.
Avoids single point of failure : can be deployed in a cluster.
TTL tuning : too short causes premature release; too long reduces safety. A watchdog mitigates this.
Replication lag : in asynchronous master‑slave setups a lock may be lost if the master fails before replication.
Cluster Considerations
Master‑Slave Failover
A lock created on the master may be lost if the master crashes before the key is replicated to slaves.
Split‑Brain
Network partitions can cause two masters to exist simultaneously, allowing two clients to hold the same lock.
RedLock Algorithm
RedLock uses multiple independent Redis nodes. A client must acquire the lock on a majority (N/2 + 1) of nodes within a time budget.
Record the current Unix time in milliseconds.
Attempt to set the same key with a unique value (e.g., UUID) on each node, using a short network timeout.
Measure the time spent acquiring the lock.
Success requires a majority of nodes granting the lock **and** the elapsed time being less than the lock’s TTL.
The effective TTL is originalTTL – elapsedTime.
If acquisition fails, delete the key on all nodes to avoid stale locks.
Redisson Library
Redisson is a Java client that provides high‑level abstractions for distributed locks, collections, and objects.
Simple usage example:
// Acquire distributed lock
RLock lock = redissonClient.getLock("myLock");
try {
// Wait up to 10 s, lock expires after 30 s
boolean locked = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (locked) {
System.out.println("Lock acquired, executing business logic...");
// business logic here
} else {
System.out.println("Failed to acquire lock...");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
redissonClient.shutdown();
}Redisson stores lock information in a hash where the field is the client identifier and the value is the re‑entrancy count, enabling re‑entrant locks.
Watchdog Mechanism
When a lock is acquired, Redisson starts a background daemon thread that automatically extends the lock’s expiration (default interval 30 seconds, configurable). This prevents premature lock release for long‑running tasks.
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.
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
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.
