不能用del直接删锁,因为del不校验持有者身份,会误删别人正在用的锁;必须用lua脚本原子执行“判断value是否匹配+删除”,确保只有加锁者才能释放锁。

Redis分布式锁为什么不能用DEL直接删锁
因为DEL不校验持有者身份,会误删别人正在用的锁。比如 A 加了锁,业务执行慢超时自动过期,B 抢到新锁;此时 A 执行完调DEL,就把 B 的锁删了——后续 C 可能立刻抢到锁,和 B 并发操作资源。
必须用 Lua 脚本原子判断+删除:
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
-
KEYS[1]是锁 key,ARGV[1]是加锁时写入的唯一 value(如 uuid) - 整个脚本在 Redis 单线程中执行,不存在竞态
- Go 中调用:
client.Eval(ctx, script, []string{key}, value)
SET命令参数怎么配才安全
别拆成 SET + EXPIRE 两步——中间网络断开或进程崩溃,锁就永久残留。必须用原子命令:
SET lock:order:123 "a1b2c3" EX 30 NX
-
EX 30:30 秒后自动过期,防死锁 -
NX:仅当 key 不存在时设置,保证互斥 - value 必须全局唯一,不能硬编码(如
"1"),否则释放时校验失效 - 推荐用
crypto/rand生成 16 字节随机串再 base64 编码
锁续期(renew)为什么不是可选而是必做
业务耗时不确定,但 Redis 锁又必须设固定 TTL。如果锁在业务中途过期,其他节点就会闯入——这不是理论风险,是线上高频事故。
- 常见错误:只靠“足够长的 TTL”硬扛,结果遇到数据库慢查询、GC STW、网络抖动就翻车
- 正确做法:启动一个后台 goroutine,每隔
TTL/3左右调用 Lua 刷新过期时间 - 刷新脚本也要校验 value:
if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("pexpire", KEYS[1], ARGV[2]) else return 0 end - 注意:续期必须在锁仍有效时进行,否则可能续了别人的锁
Redlock 算法在 Go 里真有必要用吗
绝大多数业务场景不需要。Redlock 是为容忍单个 Redis 实例故障设计的,但它依赖严格的时间同步,且在真实网络分区下无法保证安全性。官方文档已明确说明 Redlock 不提供强一致性保证。
- 如果你用的是云托管 Redis(如阿里云、AWS ElastiCache),背后已是多副本高可用,
SET ... NX EX就够用 - 真正需要 Redlock 的场景极少:自建 Redis 集群且对锁丢失零容忍(如金融级资金操作)
- 更现实的选择:用
github.com/go-redsync/redsync/v4,它封装了重试、随机延迟等工程细节,比手写安全得多
锁值生成、释放校验、自动续期这三件事漏掉任何一环,分布式锁就形同虚设。它不是“加个锁就完事”的功能,而是需要全程盯住的临界资源控制点。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











