Redis分布式锁在Go中需用SET key value EX seconds NX原子加锁,value为客户端唯一标识;释放锁必须通过Lua脚本校验value后删除;超时时间略大于业务最大执行时间;自动续期应基于PTTL检测剩余TTL并安全续期。

Redis 实现的分布式锁在 Go 里不是“直接调用一个函数就完事”,关键在于 SET 命令的原子性、过期时间控制、锁释放的安全性,以及客户端崩溃时的兜底能力。不加 GETSET 或 Lua 脚本校验,很容易出现误删他人锁的问题。
用 SET key value EX seconds NX 获取锁最稳妥
这是 Redis 官方推荐的加锁方式,NX 保证只在 key 不存在时设置,EX 避免死锁,value 必须是客户端唯一标识(比如 UUID),后续释放锁时用来比对。
- 不要用
SETNX+EXPIRE两步:中间可能被中断,导致 key 无过期时间 - value 不能用固定字符串(如
"1"),否则多个 goroutine 无法区分归属 - 超时时间要略大于业务最大执行时间,但不宜过长(比如 30s 足够多数场景)
- Go 中可用
redis.Client.Set(ctx, key, value, ttl),底层自动拼成带NX EX的SET
释放锁必须用 Lua 脚本校验 value 再删
直接 DEL key 是危险的——你拿到的锁可能已被别人续期或失效,删掉的是别人的锁。必须先 GET 再判断再删,而这三步要原子执行。
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{key}, value)执行,返回 1 表示删除成功 - 不要自己用
Get+if+Del:两次网络往返之间锁可能已变更 - 如果脚本返回 0,说明锁不属于当前客户端,不应重试删除(那是业务逻辑问题)
锁自动续期要用独立 goroutine + PTTL 心跳
业务耗时不确定时,锁过期会导致并发冲突。续期不是“每隔几秒无脑 SETEX”,而是要确认自己仍持有锁,并且只在剩余 TTL 较短时才续。
立即学习“go语言免费学习笔记(深入)”;
- 启动一个 goroutine,在锁获取成功后定期执行:
redis.Do(ctx, "PTTL", key)获取剩余毫秒数 - 若返回值 > 0 且 SETEX 或
PEXPIRE延长(注意 value 不变) - 续期前必须再次校验 value 是否匹配,防止续了别人的锁(可复用前述 Lua 脚本查 value)
- goroutine 应监听
ctx.Done(),在锁释放或上下文取消时退出
真正难的不是写几行 SET 和 EVAL,而是所有路径——加锁失败、网络超时、服务重启、goroutine panic——都得让锁状态可预测。value 唯一性、Lua 校验、续期时机,这三点漏掉任何一个,线上就容易出数据错乱。

















