Distributed Locks: Concepts, Challenges, and Implementation Strategies with Redis, ZooKeeper, and Redisson
This article introduces distributed locks, explains why they are needed in distributed environments, outlines their characteristics and requirements, and compares three implementation approaches—plain Redis, ZooKeeper, and Redisson—detailing their advantages, drawbacks, and code examples.
Distributed locks are synchronization primitives that ensure mutual exclusion across multiple nodes in a distributed system. They become essential when services run in a distributed environment and shared resources must be accessed atomically.
The article starts with a classic example where two requests concurrently modify a shared counter, illustrating the need for a lock to prevent race conditions.
Key characteristics of distributed systems are listed: scalability, high reliability, high concurrency, and cost‑effective horizontal scaling. These traits shape the design of distributed locking solutions.
Before diving into distributed locks, the article reviews traditional lock mechanisms for multithreaded and multiprocess programs, including language‑specific primitives such as sync.RWMutex in Go, ReentrantLock in Java, and OS‑level semaphores.
Three fundamental requirements for any lock are identified: a shared storage space, a unique identifier, and at least two states (locked/unlocked). Building on these, higher‑level properties such as re‑entrancy, herd‑effect avoidance, fairness, spin vs. block behavior, timeout, and high availability are discussed.
The necessity of distributed locks is justified by three points: services are already distributed, locks improve efficiency by avoiding duplicate work, and they guarantee correctness when multiple nodes act on the same data.
Four implementation families are introduced, with the article focusing on three practical solutions: naive Redis, ZooKeeper, and Redisson.
Naive Redis implementations are examined in detail. The simplest SETNX ‑then‑ DELETE approach suffers from deadlocks if the client crashes. Adding a timeout with SETEX still leaves a race window. The atomic SET key value EX N NX command (Redis ≥2.6.12) solves the race but can release another client’s lock if not careful. A Lua script that checks the lock value before deletion provides a safer release mechanism:
1 if redis.call("get",KEYS[1]) == ARGV[1] then
2 return redis.call("del",KEYS[1])
3 else
4 return 0
5 endOverall, the atomic SET … EX … NX pattern (option 3) is the most widely used due to its simplicity and low overhead.
ZooKeeper implementation leverages its strong consistency, session‑based ephemeral nodes, and watch mechanism. A lock is represented by a ZNode; the client that successfully creates the node holds the lock. By creating sequential ephemeral nodes under a common parent and watching the predecessor node, clients achieve fairness and avoid herd effects. The article provides a Maven dependency snippet for the Curator framework and a Java example that creates an InterProcessMutex:
1 public class TestLock {
2 public static void main(String[] args) throws Exception {
3 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
4 CuratorFramework client = CuratorFrameworkFactory.newClient("ip:port", retryPolicy);
5 client.start();
6 InterProcessMutex mutex = new InterProcessMutex(client, "/sunnyzengqi/curator/lock");
7 mutex.acquire();
8 System.out.println("Enter mutex");
9 Thread.sleep(10000);
10 mutex.release();
11 client.close();
12 }
13 }ZooKeeper’s watch‑based notification eliminates polling and enables more sophisticated lock types (read‑write, semaphore, etc.).
Redisson implementation builds on Redis but adds a watchdog thread that automatically extends the lock’s TTL while the client is alive, preventing accidental deadlocks caused by client crashes. Redisson also offers re‑entrant, fair, red‑lock, and read‑write variants, though it is Java‑centric.
The article concludes with a comparative table: ZooKeeper provides the longest lock duration and native watch support; Redis (plain) is the simplest to use; Redisson adds advanced lock types but is limited to Java. In terms of implementation effort, plain Redis is easiest, followed by ZooKeeper, with Redisson being the most complex.
Finally, the author reflects on the ongoing debate around the Redlock algorithm and encourages readers to keep learning from the community.
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.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
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.
