go用redis实现分布式锁的核心是避免锁失效、死锁和误删;必须用set key value ex seconds nx原子设锁,value用唯一标识,释放锁须通过lua脚本校验value后删除。

Go 语言里用 Redis 实现分布式锁,核心不是“怎么加锁”,而是“怎么避免锁失效、死锁、误删”——绝大多数线上故障都出在释放锁逻辑没做原子性校验。
Redis SETNX + EXPIRE 为什么不能直接用
很多初学者写 SETNX 成功后再调 EXPIRE,看似两步走完,实际存在竞态窗口:如果进程在 SETNX 返回 true 后、执行 EXPIRE 前崩溃或被 kill,锁就永远不超时。
- 必须用
SET key value EX seconds NX一条命令完成原子性设置(Redis 2.6.12+ 支持) - value 必须是唯一标识(比如 UUID 或
os.Getpid()+纳秒时间戳),否则释放锁时无法区分是不是自己加的 - 不要用固定 key 名,建议带业务前缀,如
lock:order:10086
如何安全释放锁(Lua 脚本是唯一靠谱方案)
释放锁的本质是:仅当 key 存在 且 value 匹配当前客户端时才 DEL。这个判断 + 删除必须原子,否则 A 客户端的锁可能被 B 客户端误删。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 必须用 Lua 脚本,例如:
if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end - Go 中调用:
redis.Eval(ctx, script, []string{lockKey}, lockValue).Int(),返回 1 表示删除成功 - 别用
GET + DEL两步,网络延迟或并发下必然出错
锁续期(renew)该不该做?什么场景下必须做
当业务处理时间不确定(比如调外部 HTTP、批量写 DB),且明显可能超过锁 TTL 时,必须支持自动续期;但续期本身又引入新风险。
- 只对“自己加的锁”续期,续期前先校验 value 是否匹配
- 推荐用独立 goroutine 每 1/3 TTL 时间检查并
GETSET更新过期时间(注意:不是EXPIRE,因为要读旧值防覆盖) - 一旦业务完成,立刻停止续期 goroutine 并释放锁,否则可能造成锁残留
- 更稳妥的做法是预估最大耗时,设足够长 TTL(比如 30s),避免续期逻辑复杂化
Redlock 真有必要吗?大多数 Go 项目其实不需要
Redlock 是为多 Redis 节点强一致性设计的,但代价高、实现复杂、且在分区网络下仍有争议。95% 的 Go 分布式服务跑在单 Redis(或哨兵/Cluster 主节点)上。
- 用单节点 Redis + 正确的
SET+ Lua 释放,已满足绝大多数库存扣减、幂等接口、定时任务去重场景 - 如果你用的是 Redis Cluster,注意 key hash slot 可能迁移,
EVAL脚本需确保所有操作在同一个 slot(用{...}包裹 key,如lock:{order}:10086) - 真正需要 Redlock 的场景极少,比如金融级跨机房事务协调——这种项目早该用 etcd 或专门的协调服务了
最常被忽略的一点:锁的 value 生产环境千万别用随机字符串,得带可追溯信息(如服务名+traceID),否则出问题时连谁持着锁都查不到。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










