导读:本期聚焦于小伙伴创作的《为什么Redis分布式锁在集群环境下会失效以及如何解决》,敬请观看详情。主从切换瞬间客户端A刚在master上拿到锁,还没同步到slave,master宕机后slave升主,客户端B在新主上也能加锁成功,这就出现了两个客户端同时持有锁。单实例的SET NX加锁在哨兵或集群架构里并不可靠。要从根本上避免这类竞态,需要理解Redlock算法基于多数派租约的核心思路,或者引入带 fencing token 的防护机制。本文对比了普通锁、红锁与数据库乐观锁在故障转移时的表现差异,并给出可落地的改进代码与选型建议,帮助构建高可用的并发控制方案。

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

为什么Redis分布式锁在集群环境下会失效以及如何解决

普通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

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。