最稳妥的go分布式锁实现是set key value ex seconds nx加锁、eval lua脚本原子解锁;必须保证value唯一、ttl合理、续期校验持有者,禁用非原子操作。

直接用 SET key value EX seconds NX 加锁、EVAL + Lua 脚本释放,是当前 Go 生态里最稳妥、兼容性最好、无需额外依赖的通用实现方式。别信“封装成一行函数就完事”的简化方案——漏掉 value 唯一性、TTL 计算或原子校验,线上必出竞态。
加锁必须用 SET 命令原子写入,不能拆成 SETNX + EXPIRE
老 Redis 实例(如 2.4/2.6)或某些云 Redis 兼容层不支持 SET ... NX EX 原生语法,Go 客户端(比如 go-redis/v9)的 SetNX(ctx, key, val, ttl) 在 fallback 模式下会先发 SETNX,再发 EXPIRE。中间若客户端崩溃或网络中断,key 就永远没过期时间,锁卡死。
- 务必显式调用
client.Set(ctx, key, value, ttl),它底层走的是SET key value EX seconds,服务端原子执行 -
value必须每个 goroutine 独立生成,推荐uuid.NewString();写死"1"或复用字符串会导致解锁时无法区分持有者 -
ttl别用time.Second * 30后误转成纳秒传给EX——go-redis的Set方法接收time.Duration,但 Redis 的EX参数单位是秒,内部会自动换算;你只要传30 * time.Second即可 - TTL 值建议设为业务 P99 耗时 × 2,例如导出接口 P99 是 800ms,就设
2 * time.Second;太短易掉锁,太长故障恢复慢
解锁必须用 Lua 脚本原子比对 value 再删,禁止 GET + DEL
这是最常踩的坑:GET 判断值相等后调 DEL,两步之间存在竞态窗口。A 拿着已过期的锁还在执行,B 查到 key 不存在抢锁成功,A 接着执行 DEL,就把 B 的锁删了。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 固定脚本内容必须是:
if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end - Go 中调用:用
redis.NewScript(unlockScript).Run(ctx, client, []string{key}, clientID),返回int64;结果不是1就代表解锁失败,得记录日志或告警 - 脚本别拼在代码里,用
//go:embed lua_unlock.lua或const声明,方便审计、灰度替换和跨项目复用 - 不要用
DEL key或client.GetDel这类伪原子操作——Redis 没有原生 GETDEL 命令,所谓“原子”只是客户端封装假象
长任务必须配看门狗续期,且续期也得校验 value
锁必须设 TTL 防死锁,但业务耗时不确定,这就要求续期。而乱续期更危险:A 续了 B 刚抢到的锁,B 还以为自己持有锁在改数据。
- 续期脚本同样要原子校验:
if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("expire", KEYS[1], ARGV[2]) else return 0 end - 续期间隔建议为
ttl / 3,比如 TTL=2s,就每 ~666ms 续一次;太密压 Redis,太疏容易因 GC 或调度延迟导致掉锁 - 续期 goroutine 必须在业务函数
return前显式stop()或cancel(),否则锁释放了,后台还在不停发EXPIRE请求,变成“幽灵续期” - 别依赖 Redis 主从同步保证锁不丢——主挂了从升主,旧锁大概率丢失,这是 CAP 下固有风险;续期本身就是为了缓解这个风险
Redlock 不是默认选项,单实例正确实现已覆盖 95% 场景
Redlock 要求 ≥3 个独立 Redis 实例、严格时钟同步、处理网络分区、应对幽灵锁……运维成本高,落地故障率不低。除非你的业务对锁丢失容忍度为零(比如金融级资金扣减),否则单节点 + 正确的 SET + Lua + 续期,足够健壮。
真正容易被忽略的点是:value 的生命周期必须与 goroutine 绑定,不能跨协程复用;TTL 计算必须基于真实压测 P99,而不是拍脑袋填 30s;Lua 脚本哪怕只改一个空格,也要重新上线验证——它不是胶水代码,而是分布式锁的原子边界。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










