Golang Redis如何做分布式锁_Golang Redis分布式锁教程【详解】
加锁必须用 SET key value EX seconds NXvalue 需唯一如 uuid解锁和续期必须用 Lua 脚本校验 valueTTL 设为业务 P99×2长任务需看门狗续期Redlock 多数场景不必要。加锁必须用 SET key value EX seconds NX别信单 SetNXGo 生态里很多教程说“用 rdb.SetNX 就行”但这是错的——老版本 Redis 不支持在 SetNX 里直接设过期时间而 Go 客户端比如 go-redis/v9的 SetNX(ctx, key, val, ttl) 是靠服务端 SET ... NX EX 命令实现的。如果你连的是 Redis 2.4 或没开 Lua 支持的旧实例它会 fallback 成两步先 SETNX再 EXPIRE中间一崩溃锁就永远卡住。value 必须是每个 goroutine 独立生成的比如 uuid.NewString()写死成 1 或复用字符串解锁时根本分不清是谁的锁EX 足够除非你要毫秒级精度那就用 PX但注意 Go 的 time.Second 别误转成纳秒传给 EX否则锁 1 秒变 10 亿秒TTL 建议设为「业务 P99 耗时 × 2」比如导出接口最长跑 1.2s就设 2 * time.Second太短会导致锁提前释放太长会让故障恢复变慢解锁必须用 Lua 脚本GET DEL 是经典竞态很多人写完 GET 判断值相等再调 DEL结果 A 拿着锁还没删B 查到过期去抢A 一删就把 B 刚 set 的锁干掉了——这不是 bug是必然发生的 race condition。唯一安全解法是原子脚本EVAL if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end 1 lock:key abc123Go 里用 redis.NewScript(unlockScript).Run(ctx, rdb, []string{key}, clientID)返回 int64非 1 就代表解锁失败得记录日志或告警别把脚本拼在代码里用 embed.FS 或 const 管理方便审计和灰度上线时热替换长任务必须配看门狗续期EXPIRE 单独调等于裸奔业务执行时间不确定锁又不能不设 TTL否则主从切换后锁丢失这矛盾只能靠续期解。但乱续期更危险A 续了 B 的锁B 还以为自己拿着锁在改数据。续期也得用 Lua 校验if redis.call(GET, KEYS[1]) ARGV[1] then return redis.call(EXPIRE, KEYS[1], ARGV[2]) else return 0 end续期间隔建议为 TTL / 3比如 TTL2s就每 600ms 续一次太密增加 Redis 压力太疏容易掉锁业务函数 return 前必须显式 stop 续期 goroutine否则锁释放了后台还在不停续——变成“幽灵续期”Redlock 不是银弹多数场景单实例正确实现就够用Redlock 看起来高大上但实际落地要连 ≥3 个独立 Redis 实例、校准时钟、处理网络分区、应对幽灵锁……运维成本远超收益。除非你有金融级一致性要求否则单实例锁只要做到 value 唯一 Lua 解锁 合理 TTL 续期已覆盖订单幂等、库存扣减、定时任务防重跑等绝大多数场景。 幻导航网 发现优质实用网站,开启网络探索之旅