redlock通过在≥3个独立redis实例上执行多数派加锁(n/2+1)来避免单点故障和主从切换导致的锁失效,要求各客户端连接物理隔离、超时严格控制,并用uuid+lua脚本保障释放安全。

Redlock 不是单个 Redis 实例的 SETNX
直接用 client.SetNX(ctx, key, "val", expire) 只在单实例上加锁,它防不了主从切换丢锁、网络分区后旧锁残留、时钟漂移导致提前过期。哪怕你用了 redis.NewFailoverClient() 或 redis.NewClusterClient(),它们内部做自动重试/转发,破坏了 Redlock 要求的「多个独立物理实例」前提——结果就是多数派判断失效,锁形同虚设。
必须显式初始化 ≥3 个独立 *redis.Client
redsync/v4 或 redlock-go 这类库不会帮你连多个地址;它只负责协调逻辑。你得手动创建至少 3 个互不共享主从关系的 *redis.Client,每个指向不同 IP:Port:
- 每个 client 的
Timeout和ReadTimeout建议 ≤ 50ms,避免某个慢节点拖垮整体判断 - 不能复用同一个连接池或共用
redis.UniversalClient—— 否则所有请求打到同一台机器,Redlock 变成 SETNX - 推荐用奇数个实例(3 或 5),便于多数派计算:
success > len(clients)/2
Lock() 成功后必须立刻读取实际剩余有效期
mutex.Lock() 返回 true,不代表你有完整 expire 时间可用。Redlock 的真实有效时间 = 最小响应 TTL − 整个加锁过程耗时。如果业务逻辑执行超时,锁会静默释放,而你的 goroutine 还在跑。
正确做法:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 调用
mutex.Expiry()获取当前剩余秒数,据此约束业务最大执行窗口 - 对长任务,需主动调用
mutex.Extend()续期,但必须检查返回值和新Expiry()—— Extend 也可能失败 - 别用
time.Sleep()测超时,GC STW 或调度延迟可能吃掉几毫秒,线上累积就超限
解锁必须用 Lua 脚本 + 客户端唯一标识
直接 Del(key) 是危险的:A 加锁、B 在 A 还没执行完时因超时释放了锁,B 的 Del 可能删掉 A 的锁。安全释放必须满足「只有加锁者才能删」。
标准做法是:
- 加锁时写入一个客户端唯一值(如
uuid.NewString()) - 释放时用 Lua 脚本原子判断:
if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end - 务必在
defer中调用 Unlock,但也要处理 panic 场景——锁靠超时兜底,但期间无通知
Redlock 的多数派机制只解决「锁获取阶段」的容错,不覆盖业务执行中的异常、续期失败或误删。真正难的不是怎么加锁,而是怎么让锁的生命周期和业务边界严丝合缝。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










