Why Distributed Locks Matter and How to Implement Them with DB, Redis, and ZooKeeper
The article explains why a simple JVM lock fails in multi‑instance services, illustrates the overselling problem with an e‑commerce example, and then compares three practical distributed‑lock solutions—database‑based, Redis‑based, and ZooKeeper‑based—detailing their mechanisms, strengths, weaknesses, and suitable scenarios.
In a distributed system, a JVM‑level lock works only within a single process, so multiple service instances cannot coordinate access to shared resources. This can cause concurrency bugs such as inventory overselling, where two instances read a stock of 1, both deduct, and the final stock becomes -1.
1. Database‑based lock
A lock table is created with a unique index on the lock name. Acquiring the lock inserts a row; the unique constraint guarantees that only one client succeeds. Releasing the lock deletes the row.
Advantages: simple, no extra middleware, leverages ACID transactions for strong consistency between lock and business data, low operational cost.
Drawbacks: each lock/unlock incurs an insert/delete, causing significant I/O; performance becomes a bottleneck under high concurrency. If a client crashes, the lock row remains and must be cleaned up by a background task, otherwise deadlocks occur. The database also introduces a single point of failure, and failover may lead to inconsistent lock state.
Suitable for low‑traffic, high‑reliability scenarios such as financial reconciliation or batch processing where consistency outweighs performance.
2. Redis‑based lock
The core command is SET key value NX PX timeout, an atomic operation that sets the key only if it does not exist (NX) and assigns an expiration (PX). The value must be a unique identifier (e.g., a UUID). To release the lock, the client checks that the stored value matches its own before deleting the key, preventing accidental deletion of another client’s lock.
Key design points:
Unique value ensures only the lock owner can release.
Expiration time avoids deadlock if the client crashes.
Advantages: in‑memory operations give tens of thousands of QPS per node, lock latency is extremely low, and mature libraries such as Redisson provide re‑entrancy, fairness, and automatic renewal.
Drawbacks: Redis replicates asynchronously; if the master crashes before replicating a lock record, a slave may be promoted without the lock, allowing two clients to hold the same lock. The RedLock algorithm mitigates this but remains debated in academia and adds implementation complexity.
Best for high‑concurrency, performance‑sensitive workloads that can tolerate a tiny probability of inconsistency, e.g., flash‑sale or rate‑limiting scenarios.
3. ZooKeeper‑based lock
ZooKeeper relies on two primitives: temporary sequential nodes and the Watch mechanism. When a client wants a lock, it creates a temporary sequential node under a designated lock path. ZooKeeper assigns an auto‑incremented sequence number.
The client lists all child nodes, sorts them, and checks whether its node has the smallest sequence number. If it is the smallest, the lock is acquired; otherwise, the client sets a watch on the predecessor node and waits for its deletion.
Releasing the lock simply deletes the client’s temporary node. If the client crashes or loses its session, ZooKeeper automatically removes the temporary node, freeing the lock without needing an expiration timeout.
ZooKeeper implements the ZAB protocol, providing strong consistency as long as a majority of nodes are alive. Temporary nodes guarantee dead‑lock avoidance, and sequential nodes give a natural FIFO ordering, preventing lock starvation.
Drawbacks: each lock/unlock requires a write that must be committed by a quorum, incurring network round‑trips and disk I/O, so latency is higher than Redis. Massive concurrent watches can cause a “herd effect” with many notifications, and the system demands more operational expertise.
Ideal for scenarios where consistency is paramount and concurrency is moderate, such as distributed task scheduling, configuration management, service discovery, or core financial transaction processing.
Choosing the Right Implementation
Use a database lock when you need the strongest data‑business consistency and traffic is modest. Choose Redis when you need ultra‑low latency and can accept a minimal risk of lock loss. Opt for ZooKeeper when strict consistency and fairness are required, and you can tolerate higher latency and operational complexity.
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.
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.
