当前位置: 首页 > news >正文

【Redis从入门到精通】第51篇:Cluster复制与故障转移——集群高可用机制

上一篇【第50篇】集群重新分片——不停服迁移槽位的黑魔法
下一篇【第52篇】sentinel vs cluster——redis高可用方案怎么选


在上一篇中,我们搞懂了Redis Cluster的重新分片机制——数据是怎么在节点之间搬家的。但你有没有想过一个问题:如果某个主库突然挂了怎么办?难道整个集群就瘫了?

当然不是。Redis Cluster内置了一套完整的主从复制 + 自动故障转移机制。今天我们就来扒一扒这套机制的底裤,看看它是怎么在主库挂掉之后,自动"扶正"一个从库上位的。


集群模式下的主从复制

复制拓扑

在Redis Cluster中,每个主库(Master)都可以有零个或多个从库(Replica/Slave)。从库实时复制主库的数据,当主库下线时,从库可以被选举为新主库。

集群复制拓扑示意 Node A (Master) Node D (Master) 槽: 0-5460 槽: 5461-10922 | | | v v v A-1 A-2 D-1 (Replica) Node G (Master) Node J (Master) 槽: 10923-16383 槽: (无,从库) | | v v G-1 (Replica) (属于其他主库)

这里有个值得注意的点:Node J没有分配任何槽位。在Cluster模式中,一个节点可以既不持有槽位,也不做任何主库的从库——但这其实是一种资源浪费,你应该避免这种情况。

配置主从复制

有两种方式建立主从关系:

方式一:使用CLUSTER REPLICATE命令

# 先连上从库节点redis-cli-p7001# 告诉它"你去给7000号节点当小弟"CLUSTER REPLICATE<node-id-of-7000># 返回 OK 表示复制关系建立成功

方式二:在启动时使用--cluster-replica参数

# 创建集群时直接指定每个主库的从库数量redis-cli--clustercreate\127.0.0.1:7000127.0.0.1:7001127.0.0.1:7002\127.0.0.1:7003127.0.0.1:7004127.0.0.1:7005\--cluster-replicas1

--cluster-replicas 1表示每个主库配1个从库。上面的命令会自动把7003、7004、7005分别配置为7000、7001、7002的从库。

查看复制关系

CLUSTER REPLICAS<node-id># 返回该主库所有从库的 node-idCLUSTER NODES# 查看所有节点及其复制关系# 输出示例:# 07c37d... 127.0.0.1:7000@17000 myself,master - 0 ... 0-5460# 67a2ab... 127.0.0.1:7001@17001 master - 0 ... 5461-10922# e685e5... 127.0.0.1:7002@17002 master - 0 ... 10923-16383# 7d3aea... 127.0.0.1:7003@17003 slave 07c37d... 0 ... 0-5460# 388f12... 127.0.0.1:7004@17004 slave 67a2ab... 0 ... 5461-10922# f2c197... 127.0.0.1:7005@17005 slave e685e5... 0 ... 10923-16383

踩坑提示:在Redis 5.0+中,配置文件和命令已经把slave相关术语改为replica。但如果你用老版本的redis-cli连接新版本,输出的CLUSTER NODES仍然会显示slave。这不是bug,是兼容性处理。


主库下线检测——从怀疑到确诊

故障检测是故障转移的前提。Redis Cluster采用了一套两阶段检测机制:先主观下线(PFAIL),再客观下线(FAIL)。这跟医生看病有点像——先觉得你不对劲,再确诊。

PFAIL(主观疑似下线)

每个集群节点每秒都会向其他节点发送PING消息(通过Gossip协议),同时等待对方的PONG回复。

如果一个节点A在cluster-node-timeout时间内没有收到节点B的PONG回复:

  1. 节点A把节点B标记为PFAIL(Possibly Failed)——“疑似下线”
  2. PFAIL状态只存在于节点A的本地视角中
PFAIL 检测流程 节点A 节点B | | |--- PING (每秒) ------->| | | |--- PING -------------->| (B没有回复) | | |--- PING -------------->| (B还是没有回复) | | | 等待 cluster-node-timeout 超时... | | | [B 被标记为 PFAIL] |

注意cluster-node-timeout的默认值是15秒。这意味着从B停止响应到A标记它PFAIL,最快也要15秒。这个值不是越小越好——设置太小会导致频繁的误判(比如网络抖动),设置太大会导致故障发现太慢。

FAIL(客观下线)

PFAIL只是"我觉得你挂了",但集群不会因为一个人的判断就采取行动。需要大多数节点都认为某个节点挂了,才能确认为FAIL。

具体流程:

  1. 节点A发现B为PFAIL后,通过Gossip协议把B的PFAIL状态传播给其他节点
  2. 每个收到PFAIL信息的节点都会记录:“A认为B疑似下线”
  3. 当一个节点C发现超过半数的主库节点都认为B是PFAIL时:
    • C将B标记为FAIL(客观下线)
    • C向集群广播一条FAIL 消息
FAIL 判定流程 时刻T0: 节点B无响应 时刻T1 (超时后): A标记B为PFAIL ─── Gossip ───> C收到: "A说B=PFAIL" D标记B为PFAIL ─── Gossip ───> C收到: "D说B=PFAIL" E标记B为PFAIL ─── Gossip ───> C收到: "E说B=PFAIL" 时刻T2 (收集到多数票): C统计: A/D/E都说B=PFAIL (3票 > N/2) C标记B为FAIL ─── 广播FAIL消息 ───> 全集群

FAIL消息的广播

FAIL消息不是Gossip消息,而是通过全员广播的方式传播:

  • 收到FAIL消息的节点会立即将B标记为FAIL,不需要等待自己的超时
  • FAIL消息会确保全集群快速达成共识

踩坑提示:FAIL消息是单向传播的。如果某个节点因为网络分区错过了FAIL消息,它会通过后续的Gossip交换来更新状态。所以最终一致性是有保证的,但可能需要一些时间。


从库投票选举——谁来当新主库

当主库被标记为FAIL后,它的从库们就要开始"竞选"了。这个过程类似于公司CEO突然离职后的紧急董事会选举。

选举流程

从库选举流程 Step 1: 自我提名 ┌──────────────────────────────────────┐ │ 从库A、从库B 检查自己是否满足选举条件 │ │ • 与主库的复制连接正常 │ │ • 主库处于FAIL状态 │ │ • 没有触发选举超时 │ │ 满足条件 → 向集群广播FAILOVER_AUTH_ │ │ REQUEST消息 │ └──────────────────────────────────────┘ | v Step 2: 集群投票 ┌──────────────────────────────────────┐ │ 每个收到AUTH_REQUEST的主库节点投票: │ │ • 对每个故障主库,只投一票 │ │ • 先到先得(谁先发请求投给谁) │ │ • 回复FAILOVER_AUTH_ACK │ └──────────────────────────────────────┘ | v Step 3: 统计结果 ┌──────────────────────────────────────┐ │ 从库收到 N/2 + 1 张ACK票: │ │ → 当选!晋升为主库 │ │ 未收到足够票数: │ │ → 下一轮再试 │ └──────────────────────────────────────┘ | v Step 4: 执行故障转移 ┌──────────────────────────────────────┐ │ 新主库: │ │ 1. 撤销对原主库的从库关系 │ │ 2. 接管原主库的槽位 │ │ 3. 广播PONG消息(让全集群知道新主库) │ │ 4. 开始处理客户端请求 │ └──────────────────────────────────────┘

选举条件详解

不是每个从库都有资格参选。必须同时满足以下条件:

条件说明
复制连接正常从库与原主库的复制连接断开时间不能超过cluster-node-timeout
主库FAIL只有当主库被标记为FAIL时,从库才会发起选举
选举延迟不是所有从库同时选举,而是按复制偏移量排序,偏移量越大延迟越短

选举延迟的计算

// 伪代码delay=500ms+random(0,500ms)// 基础延迟 + 随机抖动delay+=(rank-1)*1000ms// rank越小延迟越短// rank的计算:// 按复制偏移量从大到小排序// 偏移量最大的从库 rank=0,最小的 rank=N-1

这意味着数据最新的从库(偏移量最大)会最先发起选举,大大增加了选出"最佳候选人"的概率。

投票规则

投票有几个重要规则:

  1. 每个主库对每个故障主库只能投一票—— 不会同时给同一故障主库的两个从库投票
  2. 先到先得—— 如果从库A先发AUTH_REQUEST,主库C把票投给A,之后从库B的请求会被拒绝
  3. 只有主库才能投票—— 从库没有投票权(自己都泥菩萨过江了)
  4. 不给自己投—— 从库不给自己投

踩坑提示:如果你的集群有3个主库,其中一个主库故障后,其从库需要获得2票(3/2+1=2,向上取整)才能当选。但如果另一个主库也挂了(只剩2个主库在线),那从库只需要1票就能当选。这也是为什么建议集群至少有3个主库。


手动故障转移

有时候你不想等自动故障转移——比如你想在维护窗口期做一次计划内的主库切换。这时候可以用手动故障转移。

CLUSTER FAILOVER 命令

# 在从库上执行CLUSTER FAILOVER# 强制故障转移(即使主库还活着也转)CLUSTER FAILOVER FORCE# 带超时的故障转移(等待主库同步完成再转)CLUSTER FAILOVER TAKEOVER

三种模式对比:

选项说明主库状态要求
无参数(默认)安全模式,等主库和从库数据同步完成后再转移主库必须在线且同步中
FORCE强制模式,不等同步完成直接转移主库可以在线
TAKEOVER接管模式,从库不等其他主库投票,直接接管适用于紧急情况

手动故障转移的使用场景

# 场景1: 计划内维护(升级主库Redis版本)# Step 1: 在从库上执行手动故障转移redis-cli-p7003CLUSTER FAILOVER# Step 2: 确认转移成功redis-cli-p7003CLUSTER NODES# 确认自己变成了master# Step 3: 升级原来的主库redis-cli-p7000SHUTDOWN NOSAVE# 升级7000的Redis版本...# Step 4: 把升级后的节点作为从库加入集群redis-cli-p7000CLUSTER REPLICATE<new-master-id># 场景2: 某个主库CPU 100%,但进程还活着# 从库上执行强制转移redis-cli-p7003CLUSTER FAILOVER FORCE

踩坑提示TAKEOVER模式非常危险!它绕过了正常的投票流程,直接让从库接管。如果你在脑裂场景下使用了TAKEOVER,可能导致数据丢失。除非你确定自己在做什么,否则不要用。


故障转移超时与参数调优

cluster-node-timeout

这是集群故障检测最核心的参数,默认值为15000毫秒(15秒)

# redis.confcluster-node-timeout15000

这个参数影响着集群的多个行为:

行为计算方式
PFAIL检测时间= cluster-node-timeout
从库迁移超时= cluster-node-timeout * slave-announce-factor(已废弃)
选举超时从最后一次收到主库PONG后开始计时
故障转移完成时间约 = cluster-node-timeout + 选举时间(1-2秒)

调优建议

# 低延迟网络(同机房)cluster-node-timeout5000# 5秒# 跨机房部署cluster-node-timeout30000# 30秒# 公网部署(不推荐,但如果你非要...)cluster-node-timeout60000# 60秒

踩坑提示:不要把cluster-node-timeout设得太大。因为Redis Cluster有一个"惩罚"机制——当从库发现和主库断连超过cluster-node-timeout后,会尝试重新连接。但如果超时设为60秒,那故障发现就需要60秒,再加上选举时间,整个故障转移可能要70秒以上。对于高可用要求高的系统来说,这太慢了。

cluster-require-full-coverage

# redis.confcluster-require-full-coverageyes# 默认值

这个参数控制一个关键行为:当集群中有槽位没有被任何节点负责时,是否拒绝服务

设置行为
yes(默认)如果有任何槽位不可用,整个集群拒绝所有写操作
no只有不可用槽位的写操作会被拒绝,其他槽位正常工作
cluster-require-full-coverage = yes 时: 槽 0-5460 正常可用 ✓ 槽 5461-10922 主库挂了 ✗ 槽 10923-16383 正常可用 ✓ → 客户端写任何key都会报错: (error) CLUSTERDOWN Hash slot not served cluster-require-full-coverage = no 时: 槽 0-5460 正常可用 ✓ 槽 5461-10922 主库挂了 ✗ 槽 10923-16383 正常可用 ✓ → 只有落在5461-10922的key会报错,其他key正常工作

生产建议:如果对可用性要求极高,可以考虑设置为no。但要注意,这意味着在部分节点故障期间,有些数据可能无法访问。你需要确保应用层能处理这种情况(比如对用户显示"该功能暂时不可用")。


孤立主库与集群分区

孤立主库(no replica)

如果某个主库没有从库(no replica),那它一旦挂掉,它负责的槽位就没有节点接管了。

孤立主库风险示意 Node A (Master) Node B (Master) Node C (Master) 槽: 0-5460 槽: 5461-10922 槽: 10923-16383 从库: 无 ← 危险! 从库: D 从库: E A 挂了 → 槽 0-5460 没人管 → 集群不可用!

解决方案

# 检查是否有孤立主库redis-cli-p7000CLUSTER NODES|grep"master"|whilereadline;donode_id=$(echo$line|awk'{print $1}')replicas=$(redis-cli-p7000CLUSTER REPLICAS $node_id|wc-l)if["$replicas"-eq0];thenecho"WARNING: Node$node_idhas no replicas!"fidone

集群分区与脑裂

当网络分区发生时,集群可能被分成两个互相无法通信的部分。这就是经典的**脑裂(Split Brain)**问题。

集群脑裂场景 ┌─────────────────────────┐ 网络断开 ┌─────────────────────────┐ │ 分区1(多数派) │ ──────────── │ 分区2(少数派) │ │ │ │ │ │ Master-A (alive) │ │ Replica-A (alive) │ │ Master-B (alive) │ │ │ │ Replica-C (alive) │ │ │ │ │ │ │ │ 3个主库 → 正常工作 │ │ 0个主库 → 只读/拒绝服务 │ └─────────────────────────┘ └─────────────────────────┘ 少数派中的从库不会被选举为主库(因为拿不到多数票) 所以不会发生真正的脑裂写入

Redis Cluster通过投票机制天然防止了脑裂写入——少数派分区的从库拿不到多数票,无法被选举为新主库。这是一个非常优雅的设计。

但有一个极端情况需要警惕:

极端脑裂场景(6节点,3主3从) 分区前: A(Master) ← B(Slave) C(Master) ← D(Slave) E(Master) ← F(Slave) 网络分区后: ┌─────────────────┐ ┌─────────────────┐ │ 分区1 (2主+0从) │ │ 分区2 (1主+3从) │ │ A, C │ │ B, D, E, F │ │ 正常工作 │ │ B无法晋升(1/3票) │ │ B从库丢失 │ │ D无法晋升(1/3票) │ └─────────────────┘ └─────────────────┘ → 分区1中A和C变成了没有从库的主库 → 如果此时A或C再挂掉,对应槽位就没人管了

防御措施

  1. 确保每个主库至少有1个从库,最好分布在不同机房
  2. 使用cluster-node-timeout合理值,避免误判
  3. 设置cluster-require-full-coverage no来提高部分故障时的可用性

故障转移的完整流程总结

让我们把整个流程串起来:

Redis Cluster 故障转移完整流程 ① 正常运行 Master-A ← Replica-A1, Replica-A2 所有节点互相 PING/PONG ② Master-A 崩溃 其他节点超时后标记 A = PFAIL PFAIL 状态通过 Gossip 传播 ③ 确认故障 超过半数主库认为 A = PFAIL 触发 A = FAIL,广播 FAIL 消息 ④ 发起选举 Replica-A1(数据最新的从库)延迟最短,率先发起 广播 FAILOVER_AUTH_REQUEST ⑤ 投票 其他主库投票(FAILOVER_AUTH_ACK) Replica-A1 获得 N/2+1 票 ⑥ 执行转移 Replica-A1 撤销从库身份 Replica-A1 接管 A 的槽位 Replica-A1 广播新身份(PONG) Replica-A2 自动转为 Replica-A1 的从库 ⑦ 恢复服务 客户端重新路由请求到新主库 故障转移完成!

本章小结

Redis Cluster的故障转移机制可以总结为一句话:Gossip发现 + 两阶段确认 + 投票选举

组件作用关键参数
PING/PONG心跳检测cluster-node-timeout
PFAIL主观下线单节点判断
FAIL客观下线半数以上主库确认
AUTH_REQUEST从库发起选举复制偏移量决定优先级
AUTH_ACK主库投票先到先得,每主库一票
CLUSTER FAILOVER手动故障转移FORCE / TAKEOVER

这套机制虽然没有Sentinel那么"专职",但它是内嵌在集群协议中的,不需要额外的组件。而且通过投票机制天然防止了脑裂,这在分布式系统中是非常难得的。


上一篇【第50篇】集群重新分片——不停服迁移槽位的黑魔法
下一篇【第52篇】sentinel vs cluster——redis高可用方案怎么选


http://www.jsqmd.com/news/950015/

相关文章:

  • 别再手动拼接字节了!用C#的Socket轻松搞定HL7 MLLP协议传输(附完整代码)
  • 基于Makey Makey与Scratch的视障辅助绘画系统设计与实现
  • AI驱动的智能信托架构设计(2024监管合规版):基于银保监AI治理白皮书的12项核心指标拆解
  • LevelDB GUI管理工具完整指南:可视化键值数据库管理终极方案
  • Artisan咖啡烘焙软件终极指南:从零开始掌握专业烘焙
  • 腾讯混元 API 接入与国内模型统一入口实践:API Key、OpenAI 兼容调用、向量引擎中转配置与企业安全检查
  • 别再死记硬背了!从‘对称性’秒懂傅里叶变换中那个恼人的2π因子
  • 抖音批量下载工具:从零开始构建你的个人媒体库
  • 别再硬编码了!用两张核心表搞定所有OA审批流程(附加班申请完整SQL)
  • 如何快速掌握DSGE模型:开源工具集合的完整教程
  • 2026年广东佛山5大全屋定制家具厂家推荐!2026最新排名出炉,合禾来家具实力领先 - 十大品牌榜
  • 避开惯性导航仿真的第一个坑:手把手教你正确配置PSINS的glv全局变量(含常见错误排查)
  • 如何轻松录制40+平台直播:开源直播录制工具终极指南
  • 城通网盘解析器:3分钟快速获取直连地址的完整解决方案
  • 基于Arduino的R5-D4机器人制作:从步进电机控制到莫尔斯电码LED
  • Spek频谱分析性能调优实战指南:7个高效技巧提升大文件处理速度
  • 告别盗版素材!自带版权的科研绘图工具
  • FSearch高性能架构解析:3大核心技术实现原理与内存优化策略
  • 基于Makey Makey与Arduino的辅助沟通设备制作指南
  • 定制衣柜选板材怎么看?2026年常用品牌全维度选型指南 - 科技焦点
  • 如何高效实现Python量化交易:jqktrader智能自动化交易系统深度解析
  • EASY-HWID-SPOOFER深度解析:内核级硬件指纹伪装技术揭秘
  • UI-TARS桌面版:终极零代码GUI自动化解决方案,让AI成为你的数字操作员
  • Hudi 湖仓一体架构:阿里云 AnalyticDB MySQL 原生集成最佳实践
  • 闲置大牌首饰别乱卖!杭州正规回收门店实测对比攻略 - 奢侈品回收评测
  • LGTV Companion终极指南:让你的LG电视与Windows电脑实现智能联动
  • Swagger2Word架构解析:企业级API文档自动化转换的最佳实践
  • taskt RPA自动化工具:彻底解放你的重复性工作,免费开源的全能解决方案
  • Cursor Free VIP破解工具:如何彻底解决AI编程助手试用限制问题?
  • 避坑指南:在Docker中一次性正确配置MySQL 8.0的lower_case_table_names