Why Distributed Locks Matter: Solving Inventory Oversell with Redis and Zookeeper
The article explains how a single‑machine Java lock can cause inventory oversell in a distributed e‑commerce system, introduces the concept of a global distributed lock, and compares practical implementations using Redis (including RedLock and Redisson) and Zookeeper (with Curator), highlighting their trade‑offs and selection guidelines.
Problem Scenario
An e‑commerce service runs on a single JVM where each order checks stock stored in Redis before updating the database. Under concurrent requests, two threads can read the same stock value (e.g., 1), both decrement it, and the system ends up selling two items while only one is in stock, causing an oversell issue.
When the service is scaled to multiple machines, the native Java synchronized or ReentrantLock only protects threads within the same JVM, so the oversell problem reappears across nodes.
Why a Distributed Lock?
To guarantee that only one process modifies the stock at a time, a lock must be global and unique across all machines. The lock holder must be able to release the lock safely, and the lock acquisition must be atomic.
Redis‑Based Distributed Lock
The simplest approach stores a lock key in Redis:
SET anyLock unique_value NX PX 30000 // acquire lock (30 s TTL)
-- Lua script for atomic release --
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
endUse SET key value NX PX ttl to ensure atomic creation with expiration.
The lock value must be unique so that only the owner can release it.
Redis deployment options affect reliability:
Single‑node – single point of failure.
Master‑slave + Sentinel – lock may be lost during failover.
Redis Cluster – recommended for high availability.
RedLock algorithm (for Redis Cluster) steps:
Get current timestamp.
Try to create the lock on a majority of master nodes with a short TTL.
Consider the lock acquired if it succeeds on > N/2 nodes.
If acquisition fails, delete any partial locks.
Continuously poll until the lock becomes available.
Redisson, an open‑source Redis client, abstracts these details:
Config config = new Config();
config.useClusterServers()
.addNodeAddress("redis://192.168.31.101:7001")
.addNodeAddress("redis://192.168.31.101:7002")
.addNodeAddress("redis://192.168.31.101:7003")
.addNodeAddress("redis://192.168.31.102:7001")
.addNodeAddress("redis://192.168.31.102:7002")
.addNodeAddress("redis://192.168.31.102:7003");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("anyLock");
lock.lock(); // acquire
// ... critical section ...
lock.unlock(); // releaseRedisson’s internal watchdog automatically renews the lock’s TTL every 10 seconds, preventing accidental expiration while the lock is held.
Zookeeper‑Based Distributed Lock
Zookeeper provides ordered, ephemeral nodes and event watches, which are ideal for building a robust lock:
Create an ephemeral sequential node under /lock (e.g., /lock/0000000012).
List all children of /lock and determine the smallest sequence number.
If your node has the smallest number, you own the lock.
Otherwise, set a watch on the immediate predecessor node; when it disappears, repeat step 2.
When the lock node is deleted (session ends or explicit release), the next waiting client is notified and can acquire the lock.
Curator, a high‑level Zookeeper client, simplifies this pattern:
InterProcessMutex lock = new InterProcessMutex(client, "/anyLock");
lock.acquire();
// ... critical section ...
lock.release();Under the hood Curator creates the sequential ephemeral node, watches the predecessor, and cleans up automatically.
Comparison of Redis vs. Zookeeper
Redis : Very high performance, simple API, but relies on correct TTL handling and may suffer from split‑brain or failover issues; lock acquisition often requires busy‑waiting.
Zookeeper : Strong consistency, watches avoid busy‑waiting, but higher latency and heavier load under massive lock churn.
Recommendation
If a Zookeeper ensemble is already available, it is generally the safer choice for distributed locking because of its built‑in consistency guarantees. When only a Redis cluster exists, using Redisson (or a well‑implemented RedLock) provides a pragmatic solution with excellent throughput, provided the application can tolerate the occasional edge‑case anomalies.
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.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
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.
