redlock 更安全因其采用多数派+时间窗口机制:向≥3个独立实例发带超时set请求,成功≥(n/2)+1次且锁有效期减去网络耗时,避免单点故障与时钟漂移导致的锁失效。

为什么直接用 SET key value NX EX 不够安全
因为网络分区或进程卡顿可能导致锁过期后业务还没执行完,另一个节点拿到锁,出现双写。Redis 官方推荐的 Redlock 算法本质是「多数派 + 时间窗口」:向 ≥3 个独立 Redis 实例(不共用主从)发起带超时的 SET 请求,成功 ≥ (N/2)+1 次才算加锁成功,且锁的有效期要减去网络往返耗时。
- 单实例
SETNX只防并发,不防故障转移丢失锁(主挂了,从升主,旧锁消失) - Redlock 要求所有 Redis 实例时间大致同步(误差
- Go 生态里
github.com/go-redsync/redsync/v4是较成熟的 Redlock 封装,但默认用的是redis.UniversalClient,容易误配成单节点连接池
用 redsync 初始化客户端时最常踩的坑
很多人直接传一个 redis.NewClient() 进去,结果所有锁请求都打到同一个 Redis 实例上,完全没走 Redlock 流程——这等于白搭。
- 必须初始化多个独立的
*redis.Client(比如连 3 个不同地址的 Redis),再放进redsync.NewPool() - 每个
*redis.Client的Timeout和ReadTimeout建议设为 ≤ 50ms,否则单个节点延迟高会拖垮整个多数派判断 - 别用
redis.NewClusterClient()或redis.NewFailoverClient(),它们内部做重试和转发,破坏 Redlock 对“独立实例”的前提假设
mutex.Lock() 返回成功后,为什么业务逻辑还没执行完锁就失效了
Redlock 的有效时间 = 最小的 SET 响应中声明的 TTL − 请求全程耗时。如果业务处理超过这个值,锁自动释放,但你的 goroutine 还在跑,后续操作就失去保护。
- 调用
mutex.Lock()后,立刻用mutex.Expiry()获取实际剩余有效期,据此控制业务最大执行时间 - 对长耗时操作,得配合定期续期(
mutex.Extend()),但注意:Extend()本身也可能失败,需检查返回值和新 expiry - 别依赖
time.Sleep()模拟耗时来测试——真实场景下 GC STW、系统调度延迟都可能吃掉几毫秒,累积起来就超限
分布式锁不是万能的,哪些场景它根本压不住
Redlock 解决的是「多个服务实例对同一资源的互斥访问」,但它不保证原子性、不解决数据一致性、更不替代事务。
立即学习“go语言免费学习笔记(深入)”;
- 锁住的是 key,不是数据库行;如果业务里先查 DB 再锁 key,查和锁之间已有脏数据进来
- 若锁内发生 panic 且没 defer
mutex.Unlock(),锁会靠超时释放,但期间无任何通知,下游只能等 timeout - 跨服务协作(比如订单服务扣库存 + 支付服务发消息)不能只靠一个 Redis 锁协调,得结合 Saga 或本地消息表
真正难的从来不是怎么加锁,而是想清楚:你锁的到底是什么?它的边界在哪里?超时之后谁来兜底?










