Mastering Distributed Locks: DB, Redis, and Zookeeper Implementations Compared

This article examines the design and implementation of distributed locks using databases, Redis, and Zookeeper, outlining their core characteristics, practical code patterns, performance trade‑offs, fault‑tolerance mechanisms, and guidance on selecting the right solution for specific workloads.

dbaplus Community
dbaplus Community
dbaplus Community
Mastering Distributed Locks: DB, Redis, and Zookeeper Implementations Compared

Distributed Lock Overview

A distributed lock service coordinates concurrent access to shared resources across multiple nodes, providing exclusivity, fault tolerance, and dead‑lock avoidance. Typical desirable properties include re‑entrancy, high performance, and blocking semantics.

1. Database‑Based Lock

Implementation uses a table with a unique key_id column. Inserting a row acquires the lock; deleting the row releases it. Example schema:

CREATE TABLE distributed_lock (
  key_id VARCHAR(255) PRIMARY KEY,
  memo   VARCHAR(255),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Lock acquisition (non‑blocking tryLock) can be expressed as:

-- tryLock
INSERT INTO distributed_lock (key_id, memo) VALUES (?, ?)
ON CONFLICT (key_id) DO NOTHING;

-- unlock
DELETE FROM distributed_lock WHERE key_id = ?;

If a client crashes while holding the lock, the row persists and blocks other clients. A cleanup job should periodically delete rows older than a configurable timeout (e.g., 2 minutes):

DELETE FROM distributed_lock
WHERE created_at < NOW() - INTERVAL '2 minutes';

Performance is limited by the underlying DB transaction throughput (typically a few hundred TPS). High availability can be achieved with replication and VIP‑based master failover, but the solution still has a single‑point‑of‑failure risk.

2. Redis‑Based Lock

Redis implements a lock with the SET command using the NX (set if not exists) and PX (expire) options. The value is a random token that identifies the lock owner.

SET key_id token NX PX 30000   # ttl = 30 seconds

Acquisition and release logic:

// tryLock
String token = UUID.randomUUID().toString();
String result = redis.set(key_id, token, "NX", "PX", ttl);
if ("OK".equals(result)) {
    // lock acquired
}

// unlock
String current = redis.get(key_id);
if (token.equals(current)) {
    redis.del(key_id);
}

Because the lock expires automatically, a crashed client will eventually free the lock. However, if the TTL expires before the client finishes, another client may acquire the lock while the first client is still operating. A common remedy is a lock‑renewal thread that extends the TTL at a fraction of the original timeout (e.g., every 1/3 of the TTL) using PEXPIRE:

while (stillHoldingLock) {
    redis.pexpire(key_id, ttl);
    Thread.sleep(ttl / 3);
}

Redis provides high throughput (tens of thousands of ops/sec) but relies on in‑memory data; a Redis node crash can cause lock loss. Replication (master‑slave or cluster) improves availability, though asynchronous replication may still lose lock state on master failure.

3. Zookeeper‑Based Lock

Zookeeper offers strong consistency via the ZAB protocol, ephemeral nodes, and a watch mechanism. A client creates an ephemeral sequential node under a lock path; the client that owns the smallest sequence number holds the lock.

CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperUrl, retryPolicy);
client.start();
InterProcessMutex lock = new InterProcessMutex(client, "/my_lock_path");
lock.acquire();   // blocks until lock is obtained
// critical section
lock.release();

Ephemeral nodes are automatically removed when the client session expires, eliminating the need for explicit TTL. Watchers notify waiting clients when the lock node is deleted, enabling immediate hand‑over.

4. Comparison

Performance : Redis (in‑memory) > Zookeeper (network round‑trip) > Database (disk‑based).

Dead‑lock handling : DB – application‑level cleanup; Redis – TTL + optional renewal; Zookeeper – ephemeral nodes.

Availability : DB – replication + VIP failover; Redis – master‑slave or cluster (usually asynchronous); Zookeeper – quorum (≥ n/2 + 1) guarantees consistency.

Lock wake‑up : Zookeeper provides native watcher notifications; DB and Redis require polling or custom mechanisms.

Choose the implementation that matches workload characteristics: use Redis for high‑throughput, low‑latency scenarios; Zookeeper when strong consistency and built‑in coordination are required; and a simple DB lock for low‑concurrency cases.

References

Design discussion: http://weizijun.cn/2016/03/17/%E8%81%8A%E4%B8%80%E8%81%8A%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E7%9A%84%E8%AE%BE%E8%AE%A1/

Example Redis lock implementation: https://github.com/luoxn28/redis-lock

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Redis LockconcurrencySystem Designdistributed-lockZookeeper lockDB lock
dbaplus Community
Written by

dbaplus Community

Enterprise-level professional community for Database, BigData, and AIOps. Daily original articles, weekly online tech talks, monthly offline salons, and quarterly XCOPS&DAMS conferences—delivered by industry experts.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.