直接用go-redis/v9+lua脚本实现分布式锁即可,只需确保唯一锁值、设置过期时间、使用硬编码lua解锁脚本;避免redlock和跨机房方案,单可用区redis集群配合看门狗续期已满足95%场景。

直接用 go-redis/v9 + Lua 脚本就能跑通
别从零写锁结构体,go-redis/v9 的 SetNX 和 Eval 已覆盖全部原子操作需求。你只需要补三件事:唯一锁值、过期时间、Lua 解锁脚本——其余都是冗余封装。
-
SetNX必须传time.Duration,它会自动拼成SET key value EX seconds NX命令,避免SETNX+EXPIRE两步中断导致死锁 - 锁值不能是固定字符串,用
uuid.NewString()或fmt.Sprintf("%s:%d:%d", hostname, pid, goroutineID),否则解锁时无法校验归属 - Lua 脚本必须硬编码为常量或 embed.FS 加载,不能字符串拼接;内容固定为:
if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end
解锁失败常见原因只有两个
返回非 1 就算失败,不是“没拿到锁”而是“不是你的锁”,90% 情况下是以下两种之一:
- 业务执行超时,锁已过期被别人抢走,你还在调
Unlock→ 此时GET返回空或他人值,脚本直接 return 0 - 锁 key 写错(比如漏了命名空间前缀)、value 传错(比如用了旧缓存的 lockValue)、或 Redis Cluster 下没加 hash tag(如
{order}:123)导致路由到不同节点
长任务必须配看门狗续期,且间隔要设对
锁过期时间设 30 秒,续期间隔绝不能设成 15 秒或 25 秒——必须是 1/3,即每 10 秒续一次。这是防竞态的关键窗口。
- 续期前必须先
GET校验当前锁值是否匹配,不匹配就立即停掉 goroutine 并报错,防止续错别人的锁 - 用
context.WithCancel控制续期 goroutine 生命周期,Unlock时调cancel(),别靠超时或 defer 静默退出 - 续期失败(如 Redis 连接断)不能忽略,要主动清理本地锁状态并返回 error,否则业务逻辑可能误判“锁还在”
别碰 Redlock 或跨机房单集群方案
Redlock 在真实网络里基本失效:时钟漂移不可控、主从切换间隙、RTT 波动大,导致“多数派写入”形同虚设。跨机房部署 Redis 单集群更危险——一个机房延迟抖动,锁就失效。
- 单可用区 Redis 集群 + 正确实现的四要素(唯一 value、原子过期、Lua 解锁、看门狗续期),已覆盖 95% 场景
- 真需要跨机房强一致?换 etcd,用
clientv3.Txn+Lease,它的 CAS 天然防竞态,比拼 Lua 脚本靠谱得多 - ZooKeeper 适合选主类场景,但 Watcher 回调里不能做耗时操作,Go-zk 的
Lock封装虽好,路径没提前建好就会 panic
lock_acquire_duration_ms 和 lock_renew_failure_total 两个指标反复调。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











