Redis分布式锁必须满足四要素:唯一value标识、Lua原子校验解锁、后台goroutine定时续期、单机/代理部署避集群分片问题;缺一不可,否则锁失效。

直接用 sync.Mutex 或裸调 SETNX 做分布式抢占,线上扛不过 48 小时——前者跨进程失效,后者没校验、没续期、没唯一标识,一出超时或 panic 就锁残留、误删、抢重。
Redis 锁必须带 value 校验和 Lua 原子解锁
常见错误是先 GET 再 DEL:网络延迟或服务卡顿导致 GET 看到的是自己的 value,DEL 发出去时锁已被别人续上,结果删掉的是别人的锁。
- 加锁必须带唯一 value(如
hostname:pid:goroutineID或随机 UUID),不能写死"1" - 解锁必须走 Lua 脚本,且只在 value 匹配时才
DEL,例如:if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end -
SET key value NX PX 30000是起点,不是终点;缺 value 校验就等于没锁
长任务必须配后台 goroutine 续期
业务耗时 50 秒,锁设 30 秒过期,第 31 秒另一个实例闯入——这不是概率问题,是必然发生。尤其在 GC STW、DB 慢查询、日志刷盘等场景下极易触发。
- 启动一个独立
goroutine,每expiration/3时间调用一次PEXPIRE续期(同样需 Lua 校验 value) - 续期失败(如锁已丢失)要立即退出并报错,不能静默忽略
- 务必在
defer中停止续期 goroutine,避免 goroutine 泄漏
别在 Redis 集群或 Sentinel 上直接用单 key 锁
Redis Cluster 分片后,EVAL 脚本无法保证 KEYS 在同一节点;Sentinel 切主瞬间可能丢锁。这不是配置问题,是架构限制。
- 单机 Redis 或 Redis 代理(如 Twemproxy)可满足大多数业务,但需接受主从切换窗口期风险
- 要求强一致的场景,改用
etcd:其Lease + CompareAndSwap天然支持租约绑定与原子校验,clientv3.Grant和clientv3.OpPut(..., clientv3.WithLease(leaseID))就是为锁设计的 - 若必须用 Redis 集群,优先考虑
redsync库的 Redlock 实现,但它对时钟同步敏感,运维成本陡增
真正难的从来不是“怎么加锁”,而是“怎么证明锁还在我手上”。value 唯一性、Lua 原子性、租约续期、故障边界——这四件事漏掉任何一环,锁就只是个心理安慰。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











