bigywater.tech
Redis · 83

Redis Cluster 网络分区

网络分区是分布式系统最难的问题。Redis Cluster 的策略是:少数派一侧不能继续写。这篇不谈理论,量一个具体问题:一个被隔离的主库,在自己意识到「联系不上多数」之前,还会接受多久的写入?意识到之后它的表现如何?(我没有办法在这台 macOS 上制造真正的网络分区,用的是「暂停其余节点」的等价模拟,局限写在文中。)

4025–4434 ms
被隔离的主库上第一条被拒写入出现的时间(cluster-node-timeout 3000 ms)
543–594 条
这段窗口里(约头几秒)被接受的写入
CLUSTERDOWN
之后所有读写的错误

怎么模拟

真正的分区需要在网络层丢包(iptables、pfctl 之类),这台 macOS 上我没有权限做。等价的办法:让被观察的主库 X 保持运行,把其余所有节点用 SIGSTOP 暂停——对 X 而言,「其余所有节点都不再回应」和「被分区到少数派」是同样的。X 被一个客户端每 5 ms 写一次。局限:另一侧的节点是被暂停的,而不是活着的多数派,所以我看不到「多数派一侧同时升主、产生两个主库」的冲突,只能看到少数派一侧自己的行为。

少数派一侧:先照常写,然后拒绝

重复三次
实测 · 本机执行输出
   cluster-node-timeout = 3000 ms;重复 3 次
第 1 次:X 眼中 cluster_state 变 fail 4163 ms;X 上第一条被拒写入 4162 ms;12 秒里被接受(OK)的写入 557 条,被拒 1070 条([('CLUSTERDOWN The cluster ', 1070)]);恢复后 cluster_state = ok 用了 2965 ms
         恢复后 X 看到的角色:{7555: 'slave', 7553: 'slave', 7554: 'slave', 7552: 'master', 7551: 'master', 7550: 'master'}
第 2 次:X 眼中 cluster_state 变 fail 4441 ms;X 上第一条被拒写入 4434 ms;12 秒里被接受(OK)的写入 594 条,被拒 1030 条([('CLUSTERDOWN The cluster ', 1030)]);恢复后 cluster_state = ok 用了 3010 ms
         恢复后 X 看到的角色:{7550: 'master', 7552: 'master', 7554: 'slave', 7553: 'slave', 7555: 'slave', 7551: 'master'}
第 3 次:X 眼中 cluster_state 变 fail 4025 ms;X 上第一条被拒写入 4025 ms;12 秒里被接受(OK)的写入 543 条,被拒 1083 条([('CLUSTERDOWN The cluster ', 1083)]);恢复后 cluster_state = ok 用了 3055 ms
         恢复后 X 看到的角色:{7554: 'slave', 7555: 'slave', 7552: 'master', 7553: 'slave', 7551: 'master', 7550: 'master'}

X 的日志(第 3 次):
   configEpoch set to 1 via CLUSTER SET-CONFIG-EPOCH
   Cluster state changed: ok
   Cluster state changed: fail
   Cluster state changed: ok

三次结果稳定:X 在 4025–4441 ms 时判定自己联系不到多数主库,cluster_state 变成 fail——比 cluster-node-timeout(3000 ms)长一点,因为要先经过 PFAIL 判断。在这之前,X 一直照常接受写入(观察期里被接受 543–594 条,就是头 3–4 秒每 5 ms 写的那些);之后 X 拒绝所有读写,错误是 CLUSTERDOWN The cluster is down(1030–1083 条被拒)。

这个窗口意味着什么

如果这是一次真正的分区,多数派一侧的从库会在 kill 后约 5 秒升主(第 75 篇:从 kill 起 5.0–5.6 s),而这里被隔离主库停止写入的时间是 4.0–4.4 s——在本机这组参数下,少数派主库先停,多数派再升主,两者没有重叠。这个推断来自两个不同实验的时间对比(第 75 篇的时间是 kill -9 而不是分区,两边的时钟精度也不同),不是同一次实验里同时观察到的;但它解释了为什么默认参数下「被隔离的旧主库」通常不会和新主库同时接受写入。而窗口本身(头几秒里被接受的写入)如果分区发生的时间点恰好让多数派更早判定,仍然有丢失的可能(第 77、93 篇)。

恢复

其余节点恢复后,X 眼中的 cluster_state 约 3 秒回到 ok,角色仍是原来的 3 主 3 从,没有发生故障转移(我的解释是所有节点同时冻结、同时恢复,没有哪个先判定对方死了,没有验证)。真实的分区里,多数派一侧会在分区期间完成故障转移,愈合后旧主库变成从库(第 77 篇的场景)。

分步演示:少数派一侧的行为

Redis Cluster 网络分区
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。用 SIGSTOP/SIGCONT 暂停其余 5 个节点模拟「X 被隔离」;cluster-node-timeout 3000;X 由单个客户端每 5 ms 直连写入
    • 没有验证:真正的网络分区(丢包、单向不可达、部分节点可达)、多数派一侧同时故障转移时的冲突与数据丢失(第 77、93 篇覆盖了相关的窗口)、cluster-require-full-coverage、cluster-allow-reads-when-down、cluster-replica-validity-factor 与 cluster-slave-no-failover 的影响
    京ICP备2026067502号-1京公网安备11010502063487号