Redis Distributed Lock: Seven Correct Implementation Schemes
This article explains why distributed locks are needed in high‑concurrency scenarios, defines their essential properties, and systematically presents seven Redis‑based locking solutions—including SETNX+EXPIRE, timestamp values, Lua scripts, extended SET options, unique‑value verification, Redisson’s watchdog, and the Redlock algorithm—detailing their code examples, advantages, and drawbacks.
In high‑concurrency business scenarios such as flash sales and red‑packet grabbing, a distributed lock is required to ensure mutual exclusion, timeout release, re‑entrancy, high performance, high availability, and safety; Redis is a popular choice for implementing such locks.
What is a distributed lock? It is a mechanism that controls concurrent access to shared resources across multiple processes or machines, guaranteeing exclusive access and consistency.
Scheme 1 – SETNX + EXPIRE : Use SETNX to acquire the lock and EXPIRE to set a timeout. This approach is non‑atomic; if the process crashes between the two commands, the lock may become permanent.
if (jedis.setnx(key_resource_id, lock_value) == 1) {
expire(key_resource_id, 100);
try {
// business logic
} catch () {}
finally {
jedis.del(key_resource_id);
}
}Scheme 2 – SETNX with timestamp value : Store the expiration time inside the value and validate it on lock acquisition. It solves the non‑atomic issue but requires synchronized clocks and still suffers from possible overwrites and missing owner identification.
long expires = System.currentTimeMillis() + expireTime;
String expiresStr = String.valueOf(expires);
if (jedis.setnx(key_resource_id, expiresStr) == 1) {
return true;
}
String currentValueStr = jedis.get(key_resource_id);
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
String oldValueStr = jedis.getSet(key_resource_id, expiresStr);
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
return true;
}
}
return false;Scheme 3 – Lua script (atomic SETNX + EXPIRE) : Execute both commands in a single Lua script to guarantee atomicity.
if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then
redis.call('expire', KEYS[1], ARGV[2])
else
return 0
end;Scheme 4 – Extended SET command (SET EX PX NX) : Use the modern SET key value [EX seconds] [PX ms] [NX|XX] syntax, which is atomic. However, lock expiration may occur before business logic finishes, and another client could mistakenly delete the lock.
if (jedis.set(key_resource_id, lock_value, "NX", "EX", 100) == 1) {
try {
// business logic
} finally {
jedis.del(key_resource_id);
}
}Scheme 5 – SET EX PX NX with unique random value : Store a unique identifier as the lock value and verify it before deletion, reducing accidental unlocks but still not fully atomic.
if (jedis.set(key_resource_id, uni_request_id, "NX", "EX", 100) == 1) {
try {
// business logic
} finally {
if (uni_request_id.equals(jedis.get(key_resource_id))) {
jedis.del(lockKey);
}
}
}To make the release atomic, a Lua script can be used:
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end;Scheme 6 – Redisson framework : Redisson adds a watchdog thread that periodically extends the lock’s TTL while the owning thread is still active, preventing premature expiration.
Scheme 7 – Redlock (multi‑node algorithm) : Deploy multiple independent Redis masters; a client must acquire the lock on a majority of them within a short timeout. The algorithm ensures safety even if some masters fail.
References are provided for further reading.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
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.