Distributed Lock Implementation Strategies: Database, Redis, and Zookeeper
This article explains why distributed locks are needed beyond Java's JVM‑level locks, compares three common implementation approaches—database unique indexes, Redis SETNX, and Zookeeper ephemeral sequential nodes—and provides concrete SQL and Java code examples with analysis of re‑entrancy, release timing, and single‑point risks.
Java's built‑in locks such as synchronized and the JUC locks only work within a single JVM instance, which makes them ineffective in distributed environments where multiple processes or machines compete for the same resource. To address this, distributed locks are required.
Distributed Lock Implementation Schemes
The main solutions are:
Database‑based lock (using a unique index)
Cache‑based lock (Redis, Memcached, Tair)
Zookeeper‑based lock
Database‑Based Distributed Lock
The idea is to rely on a unique index to guarantee exclusivity. When a lock is requested, an insert is performed; if the unique index conflicts, the lock acquisition fails.
Table Design
CREATE TABLE `distributed_lock` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`unique_mutex` varchar(255) NOT NULL COMMENT 'business anti‑duplicate id',
`holder_id` varchar(255) NOT NULL COMMENT 'lock holder id',
`create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `mutex_index` (`unique_mutex`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;The unique_mutex column stores the business identifier (the object to be locked) and is protected by a unique index. holder_id records which competitor currently holds the lock.
Lock Acquisition
insert into distributed_lock(unique_mutex, holder_id) values ('unique_mutex', 'holder_id');If the INSERT succeeds, the lock is acquired; if a DuplicatedKeyException is thrown, another competitor already holds the lock.
Lock Release
delete from distributed_lock where unique_mutex='unique_mutex' and holder_id='holder_id';Releasing the lock simply deletes the corresponding row.
Analysis
Re‑entrancy: The basic scheme is non‑re‑entrant; a competitor must check the existing row and compare holder_id to allow re‑entry.
Release Timing: If a process crashes, the lock row may remain, causing a deadlock. A common remedy is to store the creation timestamp and delete stale rows after a timeout.
Single‑Point Issue: The database itself becomes a single point of failure; high‑availability solutions such as MySQL MHA are recommended.
Cache‑Based Distributed Lock (Redis Example)
Redis offers the highest performance because operations are in‑memory. The lock is implemented using the SETNX command with an expiration.
Lock Acquisition
public class RedisTool {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
/**
* Acquire lock
* @param jedis Redis client
* @param lockKey lock key
* @param requestId competitor id
* @param expireTime lock timeout (ms)
* @return true if lock acquired
*/
public static boolean getDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
return "OK".equals(result);
}
}The SET call succeeds only when the key does not exist (NX) and sets an expiration (PX). The requestId identifies the lock holder.
Lock Release
public class RedisTool {
private static final Long RELEASE_SUCCESS = 1L;
/**
* Release lock
* @param jedis Redis client
* @param lockKey lock key
* @param requestId lock holder id
* @return true if release succeeded
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
return RELEASE_SUCCESS.equals(result);
}
}The Lua script guarantees atomicity: it deletes the key only if the stored requestId matches, preventing other competitors from accidentally unlocking.
Analysis
Re‑entrancy: By checking the existing value after SET_IF_NOT_EXIST, the lock can be made re‑entrant.
Release Timing: The expiration automatically frees the lock if the holder crashes.
Single‑Point Issue: Redis should be deployed in a high‑availability cluster to avoid a single point of failure.
Zookeeper‑Based Distributed Lock
Zookeeper uses temporary sequential nodes. A client creates an ephemeral sequential node; the smallest sequence number wins the lock. Others watch the predecessor node and are notified when it disappears.
Lock/Unlock Process
Client creates an ephemeral sequential node under a lock directory.
If its node has the smallest sequence, the lock is acquired; otherwise it sets a watcher on the predecessor.
When the predecessor node is deleted, the client is notified and retries.
Unlocking deletes the client’s node, automatically releasing the lock.
Curator Implementation
// Acquire lock (supports timeout and re‑entrancy)
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
try {
return interProcessMutex.acquire(timeout, unit);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
// Release lock
public boolean unlock() {
try {
interProcessMutex.release();
} catch (Throwable e) {
log.error(e.getMessage(), e);
} finally {
executorService.schedule(new Cleaner(client, path), delayTimeForClean, TimeUnit.MILLISECONDS);
}
return true;
}Analysis
Re‑entrancy: By storing host/thread info in the lock node, a client can recognize its own lock and re‑enter.
Release Timing: Ephemeral nodes are automatically removed if the client session expires, ensuring timely release.
Single‑Point Issue: Zookeeper is typically deployed as a quorum; as long as a majority of servers stay alive, the service remains available.
Comparison of the Three Schemes
Each approach has trade‑offs: database locks are simple but suffer from I/O latency and single‑point risk; Redis offers low latency but requires careful handling of expiration and atomicity; Zookeeper provides strong consistency and built‑in session management but adds operational complexity.
Choose the solution that best fits your system’s performance, reliability, and operational requirements.
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.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
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.
