Understanding All Major Distributed Lock Implementations in One Guide
This article explains what distributed locks are, outlines their key properties, classifies implementation approaches, and provides detailed walkthroughs of MySQL, Redis, Etcd, and ZooKeeper based locks—including code examples, trade‑offs, and best‑practice recommendations.
What Is a Distributed Lock
A distributed lock synchronizes access to shared resources across multiple processes or services in a distributed system, ensuring mutual exclusion to maintain consistency.
Key Characteristics
Mutual Exclusion
Only one client can hold the lock at any moment.
Deadlock Avoidance
Locks are given a TTL (time‑to‑live) to prevent permanent deadlock, but TTL alone can cause two problems: premature expiration and accidental release of another client’s lock.
Reentrancy
The same thread can acquire the lock multiple times without causing deadlock; this is typically achieved with a reference‑count or unique identifier.
Fault Tolerance
When some lock nodes fail, the client can still acquire or release the lock by contacting multiple independent lock services.
Classification of Implementation Styles
Implementations fall into two broad categories: spin‑based (clients repeatedly poll until the lock is acquired or times out) and watch‑based (clients watch a key and are notified when the lock becomes available).
Implementation Methods
MySQL
Locks are represented by a unique row in a table. Inserting a row with a unique index acquires the lock; deleting the row releases it.
// Lock table definition
CREATE TABLE `lock_info` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`name` varchar(64) NOT NULL COMMENT 'method name',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_name` (`method_name`)
) ENGINE=InnoDB;Drawbacks include single‑point‑of‑failure on the database, the need to manage TTL manually, and poor performance under high concurrency.
Redis
Typical interview answer mentions using SETNX with an expiration, but the two‑step SETNX + EXPIRE is not atomic. The correct atomic command is: SET lKey randId NX PX 30000 Here randId is a client‑generated random string that guarantees only the lock owner can release the lock.
Lock acquisition flow (illustrated in the article):
Client1 acquires the lock.
If processing exceeds the TTL, the lock expires automatically.
Client2 acquires the same lock after expiration.
Client1 finishes and attempts to release the lock, but may unintentionally delete Client2’s lock.
Client3 can still acquire the lock, leading to inconsistency.
To release safely, the client checks the stored value before deletion:
if (redis.get(lKey).equals(randId)) {
redis.del(lockKey);
}For full atomicity, a Lua script is used on the Redis server:
if redis.call("GET",KEYS[1]) == ARGV[1] then
return redis.call("DEL",KEYS[1])
else
return 0
endRedis also supports a watchdog mechanism (e.g., Redisson) that automatically renews the lock before expiration.
When Redis is deployed in a cluster or sentinel mode, the article discusses the Redlock algorithm, which acquires locks on multiple independent Redis masters:
Client records the current timestamp T1.
It sends lock requests to at least five Redis masters, each with a short expiration.
If a majority (≥3) succeed, the client records timestamp T2; if T2‑T1 < lock TTL, the lock is considered acquired.
After using the resource, the client releases the lock on all masters.
The algorithm tolerates node failures because a majority can still grant the lock.
Etcd
Etcd provides a reliable KV store with features useful for locks: leases (TTL), revisions (global ordering), watches, and prefix queries.
Lock acquisition uses a lease to ensure automatic release on expiration, and a transaction that creates a key only if it does not already exist:
func main() {
config := clientv3.Config{Endpoints: []string{"xxx.xxx.xxx.xxx:2379"}, DialTimeout: 5 * time.Second}
client, err := clientv3.New(config)
if err != nil { fmt.Println(err); return }
lease := clientv3.NewLease(client)
leaseGrantResp, err := lease.Grant(context.TODO(), 10)
if err != nil { fmt.Println(err); return }
leaseID := leaseGrantResp.ID
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
defer lease.Revoke(context.TODO(), leaseID)
keepRespChan, err := lease.KeepAlive(ctx, leaseID)
if err != nil { fmt.Println(err); return }
go func() { for keepResp := range keepRespChan { fmt.Println("keepalive", keepResp.ID) } }()
kv := clientv3.NewKV(client)
txn := kv.Txn(context.TODO())
txn.If(clientv3.Compare(clientv3.CreateRevision("/cron/lock/job7"), "=", 0)).
Then(clientv3.OpPut("/cron/jobs/job7", "", clientv3.WithLease(leaseID))).
Else(clientv3.OpGet("/cron/jobs/job7"))
txnResp, err := txn.Commit()
if err != nil { fmt.Println(err); return }
if !txnResp.Succeeded { fmt.Println("lock busy") ; return }
fmt.Println("process task")
time.Sleep(5 * time.Second)
}The built‑in concurrency package wraps this logic in a simpler session‑mutex API.
ZooKeeper
ZooKeeper stores data as a hierarchical tree of znodes. A lock is created by an ephemeral znode (e.g., /lock) that disappears if the client session ends.
Client creates an ephemeral znode; if successful, it holds the lock.
Other clients fail to create the same znode and set a watch on it.
When the lock holder deletes the znode, ZooKeeper notifies waiting clients.
Clients retry the creation until they succeed.
ZooKeeper’s heartbeat‑based session detection automatically removes stale locks, but performance is lower than Redis and operational overhead is higher.
Conclusion
The article compares four lock implementations:
MySQL : simple but suffers from single‑point failure and poor concurrency.
Redis : fast, supports atomic commands, Lua scripts, watchdogs, and the Redlock algorithm for high availability.
Etcd : leverages leases, revisions, and watches; offers strong consistency but adds configuration complexity.
ZooKeeper : uses temporary nodes and watches; easy lock release on crash but slower and costlier to operate.
For most business scenarios, Redis is recommended because of its performance and mature tooling, while Etcd and ZooKeeper are suitable when strong consistency or existing service‑discovery infrastructure is required. The article cautions that no distributed lock is 100 % safe and that developers must consider their specific workload and failure modes.
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.
LouZai
10 years of front‑line experience at leading firms (Xiaomi, Baidu, Meituan) in development, architecture, and management; discusses technology and life.
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.
