结论:etcd 分布式锁必须组合 lease、txn、watch 和有序 key 设计,仅用 put+withlease 或 compareandswap 无法保证互斥;因 put 不检查 key 是否存在,多个客户端会同时写入成功,需用 txn 的 compare(version, "=", 0) 实现“仅当 key 不存在时写入”,并配合 lease 续期、watch 前序 key 实现公平唤醒与自动清理。

直接说结论:不能用 clientv3.Put 裸写 key,也不能只靠 CompareAndSwap 事务就完事——必须组合 Lease、Txn、Watch 和有序 key 设计,否则两个客户端会同时抢锁成功。
为什么 etcd 的 Put + WithLease 不能直接当锁用
常见错误是这样写:
_, err := client.Put(ctx, "/lock/order", "holder-123", clientv3.WithLease(leaseID))
问题在于:没做前置检查。多个客户端并发执行这段代码,都会成功写入,全部认为自己拿到了锁。
- etcd 不会自动拒绝重复写入;它只保证单次操作原子性,不保证业务逻辑互斥
-
WithLease只负责过期清理,不参与竞争判定 - 真正需要的是“仅当 key 不存在时才写入”,这必须用
Txn+Compare(Version(key), "=", 0)
加锁必须用 Txn + Compare + WithLease
正确姿势是把“检查 + 写入”打包成一个事务:
txnResp, err := client.Txn(ctx).
If(clientv3.Compare(clientv3.Version("/lock/order"), "=", 0)).
Then(clientv3.OpPut("/lock/order", "holder-123", clientv3.WithLease(leaseID))).
Else(clientv3.OpGet("/lock/order")).
Commit()
关键点:
-
Compare(clientv3.Version(key), "=", 0)表示“该 key 当前 version 是 0”,即从未被创建过 -
Then分支成功才真正写入,且绑定 lease;Else分支可顺便读出当前持有者,方便排查 - lease ID 必须由
client.Grant(ctx, ttl)申请,不能手动生成字符串或时间戳——否则续期失败、自动清理失效 - TTL 建议 ≥10s,续期间隔 ≤TTL/3(如设 15s TTL,则每 5s 续一次)
抢锁失败后,别轮询,要用 Watch 监听前序 key
etcd 锁的公平性靠有序 key 实现,比如用时间戳+随机数生成唯一后缀:/lock/order/1678901234567890123。抢锁失败时,不是睡几百毫秒再试,而是:
- Get 所有
/lock/order/前缀下的 key,按 key 字典序排序 - 找到自己 key 的前一个(比如你的是
/lock/order/1678901234567890123,就监听/lock/order/1678901234567890122) - 调用
client.Watch(ctx, prevKey, clientv3.WithPrevKV(), clientv3.WithRev(revision)),监听其删除事件 - Watch channel 收到响应后,立刻重试抢锁——不是等超时,是“锁一释放就唤醒”
注意:Watch 可能断连,必须捕获 context.DeadlineExceeded 或连接错误,并用 WithRev 指定起始 revision 重连,否则漏事件。
持有锁期间必须 KeepAlive,释放锁必须 Revoke
lease 过期 ≠ key 删除延迟,而是 key 立刻失效。但客户端得主动保活:
- 启动
client.KeepAlive(ctx, leaseID),它返回 - 在 goroutine 中消费该 channel,收到响应说明续期成功;若 channel 关闭或返回
nil,说明 lease 已过期,应立即放弃锁 - 释放锁不是删 key,而是调
client.Revoke(ctx, leaseID)——etcd 会自动清理所有绑定该 lease 的 key - 别在 defer 里无条件
Revoke:如果锁已被别人抢走,你的Revoke会误删别人的 key
最易忽略的是:goroutine 泄漏。每个锁实例要绑定自己的 ctx,并在释放锁时 cancel(),否则 KeepAlive 协程一直挂着。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











