在构建高并发系统时,Redis常被用来实现分布式锁以协调多个节点对共享资源的访问。然而当Redis以主从、哨兵或Cluster模式部署而非单点时,原本在单实例上看起来可靠的加锁逻辑,可能在故障切换的瞬间失去互斥性。这种现象并不是代码写错了某一步,而是架构模型与一致性保证之间出现了缝隙。只有厘清锁的生效边界,才能选对方案。

普通Redis锁在主从架构中为何会失效
最常见的Redis锁写法是用SET key value NX PX milliseconds命令,在单实例上它能保证只有一个客户端写成功从而拿到锁。但在主从复制模型中,写操作默认是异步复制到从节点的。假设客户端C1在master上成功加了锁,此时master还没把这个写命令发给slave就突然宕机,哨兵把slave提为新的master。由于新master里根本没有那条锁记录,客户端C2向新master发起同样的SET NX也能成功,于是C1和C2同时以为自己持有锁,临界区被并发进入。
这种失效和锁的过期时间长短无关,纯粹是复制延迟加故障转移带来的状态丢失。即便把复制改成同步,Redis本身也没有像ZooKeeper那样的顺序一致性协议,同步复制也会拖慢性能并可能在网络分区时不可用。很多团队在压测阶段用单实例Redis验证锁逻辑无误,上线切到哨兵集群后偶尔出现重复执行任务,根因往往就在这里。
下面是一段典型的单实例锁获取代码,它在集群下并不可靠:
// 不可靠的集群环境锁示例
public boolean tryLock(Jedis jedis, String lockKey, String requestId, int expireMs) {
// 只在当前连接的master上设置,不保证复制到从节点
String result = jedis.set(lockKey, requestId, "NX", "PX", expireMs);
return "OK".equals(result);
}
Redlock算法如何通过多数派机制弥补缺陷
Redlock是Redis作者提出的分布式锁算法,思路是不依赖单一master,而是向多个独立的Redis主节点(通常是5个)依次申请锁。客户端只有当在超过半数节点(如3个)上成功加锁,且总耗时小于锁过期时间,才认为真正拿到锁。这样即使其中一个节点宕机或切换,只要多数派还保留锁标记,其他客户端就无法在剩余节点上凑齐多数派,从而维持互斥。
Redlock还要求客户端在释放锁时向所有节点发送删除指令,而不只是当初成功的那些。因为网络抖动可能导致某些节点实际上加了锁但客户端没收到回应,后续清理能降低残留锁干扰。不过Redlock也并非完美,它需要节点间时钟大致准确,且假设节点崩溃后不会立即恢复并丢失内存数据。在强一致要求极高的场景,还需要配合 fencing token 来防止旧锁持有者恢复后乱写。
以下代码展示了简化版的Redlock加锁流程:
// 简化Redlock客户端逻辑
public boolean redLock(List<Jedis> nodes, String lockKey, String reqId, int expireMs) {
int success = 0;
long start = System.currentTimeMillis();
for (Jedis node : nodes) {
try {
// 向每个独立master尝试加锁
if ("OK".equals(node.set(lockKey, reqId, "NX", "PX", expireMs))) {
success++;
}
} catch (Exception ignored) {}
}
long cost = System.currentTimeMillis() - start;
// 多数派且未超时才算成功
return success > nodes.size() / 2 && cost < expireMs;
}
替代方案与选型建议
如果业务能接受稍弱的一致性,可以继续用单节点Redis锁并把关键资源操作做成幂等,这样即便极端情况下锁失效,重复执行也不会破坏数据。另一种做法是引入ZooKeeper或etcd,它们基于一致性协议在选举和写入顺序上有更强保证,适合对锁可靠性要求极高的金融类业务,但运维复杂度和延迟也更高。
还可以在应用层加 fencing token:每次拿锁时由一个单调递增的服务发号,写资源时带上token,存储端只接受比已处理token更大的请求。这样就算旧锁失效,迟到的新请求也会因token过小被拒绝。下表对比了几种方案的特点:
| 方案 | 一致性 | 运维成本 | 适用场景 |
|---|---|---|---|
| 单实例Redis锁 | 弱 | 低 | 幂等任务、缓存防击穿 |
| Redis集群普通锁 | 弱,易失效 | 中 | 不推荐用于互斥 |
| Redlock | 较强 | 中 | 一般分布式协调 |
| ZooKeeper锁 | 强 | 高 | 核心交易互斥 |
实际选型时,建议先问清楚业务能否容忍极小概率的锁重叠。若能容忍,单实例加幂等最省事;若不能,Redlock已是Redis体系内较优解,再往上就直接换强一致协调服务。切忌在哨兵集群上直接用普通SET NX锁还指望它安全。
Redisdistributed_lockRedlock修改时间:2026-08-13 15:39:32