ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

MySQL一主三从高可用实战:Orchestrator+ProxySQL切换全复盘

MySQL一主三从高可用实战:Orchestrator+ProxySQL切换全复盘 上周线上把 MySQL 5.7 一主三从集群做了一次真实的主库宕机演练。整套高可用方案使用的是 Orchestrator 负责自动化主从选举与切换ProxySQL 负责读写分离和入口流量调度从 kill 掉主库进程到应用侧写入完全恢复整个过程大概 14 秒。这篇文章不是功能清单式的介绍而是把这套架构从“为什么这么选型”、到一主三从在 TB 级数据下怎么初始化、再到故障切换的完整链路、以及上线后踩过的坑完整复盘一遍。如果你正在评估或者准备落地 OrchestratorProxySQL这篇可以直接当实施参考。1. 为什么我没有选 MHA 或 Keepalived而是 Orchestrator ProxySQL很多朋友一提到 MySQL 高可用第一反应就是 MHA或者 Keepalived 挂一个 VIP。这些方案不是不能用只是在 TB 级、一主三从、需要读写分离的线上环境里它们各自的短板会比较明显。我先把选型过程写清楚方便你判断自己的场景是不是适合这套组合。1.1 MHA 方案的问题在哪MHA 的核心能力是在主库故障时自动找到 relay log 最完整的从库补全缺失的 binlog 后提升为新主并把其他从库重新指向它。这套逻辑很成熟早期我也用它搭过不少环境日常故障切换确实能跑。但放到这套集群里有两个实际痛点。第一MHA Manager 本身是单点的。虽然可以想办法做双机但官方对 Manager 高可用的支持一直比较弱一旦 Manager 所在机器挂了切换就没人触发。第二MHA 只负责“数据库内部的角色切换”不负责应用侧流量调度。切换完成后应用连的还是原来的主库地址如果主库 IP 变了或者故障节点还在那要么等着连接超时要么还得靠额外脚本去改应用配置。我们这套集群每天读写请求量很大不可能接受切换完数据库角色后还要人工把应用流量切到新主。1.2 Keepalived VIP 为什么也排除了Keepalived VIP 的思路是把一个虚拟 IP 挂到主库上主库挂了 VIP 漂移到从库。它解决的是“连接地址漂移”的问题但本身对 MySQL 主从状态一无所知。直接用 VIP 做主从切换很容易出现一种很尴尬的情况主库只是 MySQL 进程假死网络还在VIP 不漂移但业务已经写不进去了或者主库彻底宕机VIP 漂到一台 relay log 明显落后的从库上丢数据的风险完全不可控。更重要的是Keepalived 是二层网络里做地址漂移的跨机房、跨网段的时候不好用。我们后期计划把从库放到另一个机房做异地容灾单独靠 VIP 根本覆盖不了这种拓扑。它作为一个“前置入口漂移”的辅助手段可以但承担不起完整的故障决策。1.3 这套组合的核心分工最后定下来的架构很清晰Orchestrator 负责决策层ProxySQL 负责数据面流量调度。Orchestrator 会持续监控所有 MySQL 实例的健康状态、主从关系、复制延迟一旦判定主库不可用自动完成候选从库选择、新主提升、其余从库重指向、旧主恢复后的收敛这些操作ProxySQL 则独立于数据库角色去管理应用连接通过 hostgroup 和查询规则把读流量和写流量分流数据库切换后应用不需要改任何代码。为什么这两个工具组合起来特别适合一主三从因为 Orchestrator 有很成熟的外部 hook 机制切换完成后可以执行自定义脚本把新主的信息同步给 ProxySQL而 ProxySQL 的管理接口又允许在运行中动态修改后端节点并立即生效。一个负责“选谁当主”一个负责“把流量发给谁”两者通过一个脚本串起来切主对应用来说是无感的。这套思路后来也成了我们做故障演练的基础。2. 一主三从初始化TB 级数据怎么在不停业务的情况下把三台从库补起来标题里写了 TB 级这一步必须单独拿出来讲。因为数据量一旦到了 TB 级别很多东西跟小库完全不是一个玩法。比如你不可能用 mysqldump 去导全量数据那得导到天荒地老也不可能停库拷贝数据文件业务根本不允许。我们当时的办法是用 Percona XtraBackup 2.4 做物理备份从一个现网从库拉全量再配合 GTID 自动位点追同步。2.1 集群规划与基础参数先说我用的版本和硬件规划方便你对照。MySQL 用的是 5.7.4x一主三从四台机器都是独立的物理机磁盘是 SSD 阵列。主库和三个从库都开启 binlogserver_id全局唯一核心参数大概是这样[mysqld] server_id 101 gtid_mode ON enforce_gtid_consistency ON log_bin mysql-bin binlog_format ROW log_slave_updates ON read_only ON super_read_only ON主库那台要允许写入所以把read_only和super_read_only注释掉只保留 binlog 和 GTID 相关配置。这里有个小细节即使是从库我仍然要求log_slave_updates ON否则从库不会把从主库同步过来的事务写进自己的 binlogOrchestrator 在后续切换中很难判断谁的事务最完整这个坑我后面会详细说。2.2 物理备份同步限速、断点续传和备份校验TB 级数据做初始化最怕的就是备份过程把线上 IO 打满。我的做法是从一台从库拉备份而不是从主库这样能尽量减少对主库的影响。备份命令类似这样xtrabackup --backup \ --target-dir/data/backup/full_$(date %Y%m%d) \ --host127.0.0.1 \ --userbackup \ --passwordbackuppass \ --slave-info \ --safe-slave-backup \ --throttle200--throttle200是 XtraBackup 自带的 IO 限速参数单位是每秒多少次 IO 操作。具体值要根据线上磁盘负载去试我们的机器压到 200 的时候主库 IO 基本没明显波动备份速度大概能跑到每小时 600GB一个 TB 多的全量两到三个小时能备份完成。备份文件很大不可能直接在本地流式传输我一般用xbstream先打包再通过pv限速推到目标从库xtrabackup --backup \ --streamxbstream --target-dir/data/backup/full_xxx \ --throttle200 2/tmp/backup.log | \ pv -L 80m | ssh root10.0.0.12 cat /data/restore/full_xxx.xbstreampv -L 80m把网络传输限到 80MB/s避免带宽被占满影响应用。限速的好处是初始化整个过程可以放在业务高峰期之外慢慢跑不需要为了赶时间让线上环境承担风险。文件传过去之后先校验备份日志确认没有异常再解流xbstream -x -C /data/restore/full_xxx /data/restore/full_xxx.xbstream xtrabackup --prepare --target-dir/data/restore/full_xxx这里必须说一个容易忽略的点如果是从从库拉的备份去初始化新从库prepare的时候不需要加--apply-log-only只要不是做增量合并直接准备到位就行如果后面想在备份基础之上再合并增量才需要--apply-log-only。准备完成后用--copy-back把文件放回数据目录调整属主后启动 MySQL。2.3 GTID 建立复制以及为什么这段必须用 GTID数据文件恢复好以后启动 MySQL直接从备份集里的xtrabackup_info或xtrabackup_binlog_info拿位点和 GTID 集合。在 GTID 模式下建立复制的命令比传统 binlog 位置简洁得多CHANGE MASTER TO MASTER_HOST10.0.0.10, MASTER_PORT3306, MASTER_USERrepl, MASTER_PASSWORDreplpass, MASTER_AUTO_POSITION1; START SLAVE; SHOW SLAVE STATUS\G只要Seconds_Behind_Master在持续下降并且最终接近 0主从就正常了。为什么这里我坚持用 GTID因为后面 Orchestrator 做切换时候选从库的选择和其余从库的重指向都依赖 GTID 自动定位。传统 binlog 位点模式下每次切主都要人去确认每个从库应该指向哪个位点错一步就是数据不一致GTID 模式下Orchestrator 可以直接说“谁的事务集合最完整”新主一确认其余从库MASTER_AUTO_POSITION1就能自动对齐。一主三从这种规模靠手工维护位点是撑不住的。另外要说一个第一次初始化容易撞上的问题复制完成后如果从库的server_uuid和源实例重复MySQL 会因为 UUID 冲突直接启动失败。XtraBackup 在备份时一般不会把auto.cnf一起恢复但如果你手工拷贝过数据文件一定要检查${datadir}/auto.cnf如果存在且和主库 UUID 相同直接删掉再重启 MySQL让它重新生成一个新的。这个问题出现过不止一次报错信息还不明显容易排查半天。3. Orchestrator 的部署和切换链路从发现故障到发起选举Orchestrator 是这套高可用里的“大脑”。它本身不处理业务流量只干一件事持续监控拓扑里所有 MySQL 实例发现主库故障后自动执行恢复流程。部署本身不复杂但有几个设计点直接决定了切换的可靠性。3.1 Orchestrator 自身的可用性三节点 Raft 比单机强在哪很多人搭 Orchestrator 只在单机跑一个实例这是比较危险的做法。Orchestrator 自己挂了故障切换就停了和 MHA Manager 单点的问题一样。我这里是三个节点组成了 Raft 集群数据层用一个独立的小 MySQL 实例存 Orchestrator 的元数据。三节点 Raft 的核心价值是避免误判。单个 Orchestrator 节点可能因为网络抖动、负载高或者其他原因误以为主库挂了但集群模式下至少要多数节点都判定主库不可达才会真正触发切换。这个设计对一主三从这种生产环境非常重要因为误切换造成的危害往往比不切换还大。你想想如果只是 Orchestrator 到主库的网络闪断了一下结果主库被切掉了全部写流量瞬间打到一台从库上这是完全不能接受的。部署时需要注意三个 Orchestrator 节点之间的网络延迟要低最好在同一个机房的同一网段Raft 数据目录要放到持久化磁盘元数据库不能跟着 Orchestrator 一起挂否则集群虽然活着但无法读取状态。我们为了让元数据库也可靠把它的主从状态通过同一套监控盯起来了属于基础设施的基础设施不敢省。3.2 关键配置项里容易踩的部分Orchestrator 的配置文件是/etc/orchestrator.conf.json。下面这段是我实际用的把关键项都标出来了{ ListenAddress: :3000, MySQLTopologyUser: orc, MySQLTopologyPassword: orcpass, BackendDB: mysql, BackendDSN: orcmeta:orcmetapasstcp(10.0.0.9:3306)/orchestrator?timeout5s, RaftEnabled: true, RaftDataDir: /var/lib/orchestrator, RaftBind: 10.0.0.7, RaftNodes: [10.0.0.7, 10.0.0.8, 10.0.0.9], DetectClusterAlias: true, RecoverMasterClusterFilters: [*], RecoveryPeriodBlockSeconds: 30, PostMasterFailoverProcesses: [ bash /usr/local/orchestrator/scripts/failover_to_proxy.sh {failureType} {destinationHost} {destinationPort} ] }Orchestrator 用来连接 MySQL 拓扑的账号权限要谨慎给不要直接给 root。我的做法是单独建orc账号授权CREATE USER orc% IDENTIFIED BY orcpass; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT, PROCESS, SUPER ON *.* TO orc%;SUPER权限是必须的因为 Orchestrator 需要执行START SLAVE、STOP SLAVE、CHANGE MASTER TO、修改read_only这类操作。一开始我尝试去掉这个权限结果切换到一半就报权限不足整个恢复流程直接卡住后来老老实实加回去了。还有一个重要配置是RecoveryPeriodBlockSeconds。它表示同一集群在一次切换后的多少秒内不会再次触发恢复避免频繁抖动导致集群状态反复横跳。我设成了 30 秒实际线上如果旧主恢复后一直不稳这个值可以再调大比如 60 秒保证有足够时间人工介入。3.3 故障切换的判定与候选从库规则Orchestrator 对主库的健康检查用的是 MySQL 原生连接每秒执行一次简单查询。连续探测失败后会进入恢复流程。恢复过程大致是确认主库状态收集所有从库的复制状态和 GTID 集合。从候选从库里选出一个 GTID 集合最完整、复制延迟最低的实例。停止该从库的复制线程执行STOP SLAVE等待 relay log 应用完毕。把该从库提升为新主SET GLOBAL read_onlyOFF、SET GLOBAL super_read_onlyOFF。把其他从库通过CHANGE MASTER TO ... MASTER_AUTO_POSITION1重新指向新主。如果旧主恢复了尝试把它也变成新主的从库并强制设成只读防止脑裂。这个链条里最关键的是第二步“选候选人”。Orchestrator 默认会根据 GTID 集合和延迟来选但实际生产环境里我们更希望让指定的一两台机器优先成为新主而不是随便选。这可以在实例上设置 promotion rule 来实现。比如让10.0.0.11这台配置最好、和主库同机房的机器作为首选新主我是通过 Orchestrator 的 web 界面把它的 promotion rule 设为prefer把一台性能较差的从库设为must_not这样即使它的 GTID 不是最新Orchestrator 也不会选它当主。4. ProxySQL 读写分离实现与自动感知切换Orchestrator 解决了“谁是新主”的问题但应用侧的流量不会自动知道新主是谁。这就是 ProxySQL 的工作。ProxySQL 作为应用和 MySQL 之间的接入层它接收来自应用的所有数据库连接再根据 hostgroup 和查询规则把语句分发到具体的后端实例。4.1 ProxySQL 部署后的核心配置ProxySQL 版本 2.x 有两个端口6032 是管理端口6033 是业务端口。我所有配置都是通过管理端口改的这样可以避免去手动改配置文件再重启进程。先配置后端节点INSERT INTO mysql_servers(hostgroup_id, hostname, port, weight, comment) VALUES (10, 10.0.0.10, 3306, 100, writer node); INSERT INTO mysql_servers(hostgroup_id, hostname, port, weight, comment) VALUES (20, 10.0.0.11, 3306, 100, reader node 1); INSERT INTO mysql_servers(hostgroup_id, hostname, port, weight, comment) VALUES (20, 10.0.0.12, 3306, 100, reader node 2); INSERT INTO mysql_servers(hostgroup_id, hostname, port, weight, comment) VALUES (20, 10.0.0.13, 3306, 100, reader node 3);这里我用 hostgroup 10 表示写组hostgroup 20 表示读组。配置账号和路由规则INSERT INTO mysql_users(username, password, default_hostgroup) VALUES (appuser, apppass, 10); INSERT INTO mysql_query_rules(rule_id, active, match_pattern, destination_hostgroup, apply) VALUES (1, 1, ^SELECT.*FOR UPDATE, 10, 1); INSERT INTO mysql_query_rules(rule_id, active, match_pattern, destination_hostgroup, apply) VALUES (2, 1, ^SELECT, 20, 1); INSERT INTO mysql_query_rules(rule_id, active, match_pattern, destination_hostgroup, apply) VALUES (3, 1, .*, 10, 0);SELECT ... FOR UPDATE必须走写组因为这种查询本质上是在锁定行如果放读组去执行锁的语义就没了。普通SELECT走读组剩下的全部走写组兜底。配置完记得执行LOAD MYSQL SERVERS TO RUNTIME、LOAD MYSQL QUERY RULES TO RUNTIME、LOAD MYSQL USERS TO RUNTIME否则不会生效。4.2 让 ProxySQL 在切换后自动跟着新主走很多教程会提到mysql_replication_hostgroups这个表让 ProxySQL 通过检测实例的read_only状态自动把节点分到写组或读组。这个方案理论可行但我实际用下来发现它依赖监控线程的扫描周期新主提升后read_onlyOFF和旧主恢复后被设成read_onlyON这两个状态变化不一定能立刻被捕捉到期间流量可能短暂走错节点。我采用的方式更直接在 Orchestrator 完成主从切换后通过PostMasterFailoverProcesses调用一个脚本脚本直接改 ProxySQL 里写主机组的节点 IP然后立刻加载到 runtime。脚本内容大致如下#!/bin/bash # 参数顺序按当前版本实际打印参数为准首次接入时先打印一次再定 NEW_MASTER$3 NEW_PORT$4 mysql -h 127.0.0.1 -P 6032 -uadmin -padmin -e UPDATE mysql_servers SET hostname${NEW_MASTER}, port${NEW_PORT} WHERE hostgroup_id10; LOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK; 你看到的$3、$4的位置要特别谨慎。不同版本的 Orchestrator 传给PostMasterFailoverProcesses的参数顺序不完全一样我在本地接脚本时专门打印了所有参数确认新主 IP 和端口在第几个位置才定下来。如果你装的是别的版本第一次上线前一定要模拟一次切换把这个地方验证透。为什么我不写死旧主 IP因为如果旧主恢复后被重新纳入集群它的 IP 可能还在写组里但角色已经变成从库。如果不改掉ProxySQL 会把写流量同时发给两个节点造成脑裂。通过脚本把写组里的 IP 直接替换成新主 IP旧主即使在mysql_servers表里也只会放在读组。4.3 应用层需要注意的配合ProxySQL 切换后理论上应用不需要改任何代码但连接池里的长连接不会立刻断开。ProxySQL 在探测到后端不可用后会逐步淘汰这些连接应用下一次从连接池取连接时重新经历认证和路由自然就走到新主了。为了加快这个收敛过程我在应用侧的数据库连接池里把连接存活时间设成 30 秒以内并且开启空闲连接检测避免某些老连接一直占着旧主的资源。另外要提醒一点transaction_persistent这个规则要慎用。我们最初为了解决“事务内先写后读被分发到从库导致数据不一致”的问题给事务开启了 persist 模式结果所有事务里的读也被锁定到写组读从库的利用率反而降下来了。后来在应用代码里强制要求事务内不要混用实时读这才彻底解决。如果你们的业务事务逻辑比较复杂先把事务和读写分离的边界理清再上 ProxySQL否则排查问题会很痛苦。5. 故障演练把主库直接 kill 掉的完整过程配置再好看不演练等于没配置。故障演练是所有高可用方案上线前必须做的一步而且不能只在测试环境做要在业务低峰期、有监控陪跑的情况下对生产环境做真实的切换验证。我们那次演练是在凌晨两点业务量最小但流量入口都是真的。5.1 演练前的准备和检查清单演练前我把状态检查做成了一份清单逐项核对过Orchestrator web 界面能看到完整拓扑一主三从状态正常没有报错。三台从库SHOW SLAVE STATUS里Seconds_Behind_Master都是 0。ProxySQL 监控账号能正常连到所有后端节点管理接口能执行修改。failover_to_proxy.sh已经手动执行过一次确认脚本本身没有语法错误。应用侧连接池参数已经调好准备接受短暂的写连接抖动。监控告警里关于主库宕机的通知暂时设成静默避免大半夜吓到值班同学。这里有一项往往被人忽略脚本要先用“假参数”在测试环境里跑一遍确认能改库、能加载到 runtime、能 save 到 disk。我第一次写脚本时直接在生产上跑结果因为 ProxySQL admin 的密码在脚本里写错了执行报错整个切换链路直接断掉。虽然这是小问题但在真实故障时会变成致命的。5.2 主库宕机后的时间线和系统行为实际演练时我在主库上直接执行了kill -9模拟最严重的进程级故障。下面是整个时间线时间点事件00:00:00主库 MySQL 进程被杀应用写请求开始报错00:00:02Orchestrator 第一次探测失败进入确认阶段00:00:05多节点确认主库不可达触发恢复流程00:00:08候选从库被选中停止复制并回放完 relay log00:00:10新主read_only被关闭其余从库重新指向新主00:00:12PostMasterFailoverProcesses脚本执行ProxySQL 写组切换到新主00:00:14应用新连接全部走到新主写入恢复整个切换过程大约 14 秒其中数据库角色切换只花了 8 秒剩下几秒是等待 relay log 追平和流量刷新。很多系统显示“秒级切换”实际上说的是探测器发现问题到选出新主的时间真正要等到 relay log 全部应用、ProxySQL 刷新生效业务才能恢复。这个时间受从库的积压和硬件性能影响很大像我们这次没有明显积压所以 14 秒完成如果从库本身延迟好几秒切换时间也会相应拉长。5.3 演练中暴露出的两个问题第一次演练其实不是一次过的暴露了两个当时没注意到的问题。第一个问题是其中一台从库在切换完成后复制线程一直显示NO。原因是它和新主的 GTID 集合不完全匹配Orchestrator 虽然已经发了CHANGE MASTER TO MASTER_AUTO_POSITION1但该从库的 relay log 里还残留着一些旧主的事务这些事务在 GTID 集合里已经存在所以从库认为没有新事务要执行直接卡住。排查方法是在从库上执行STOP SLAVE; START SLAVE;如果还不行就需要RESET SLAVE强制重新初始化复制。这里有个原则在 GTID 环境下不要轻易去手工指定位点否则很容易破坏 GTID 集合的完整性正确做法是让从库从新主重新拉全量或者重置复制状态。第二个问题比较隐蔽切换完成后应用侧有大量长连接没有立即感知到新主导致部分写请求在旧主连接上持续超时。监控里看到这段时间内连接池里的borrow失败率和timeout明显飙升。后来我在应用连接池里配置了连接最大存活时间和连接有效性探测才把这个收敛时间从几十秒缩短到几秒。为什么这时候才注意因为测试环境连接量小根本看不出来连接池回收慢的后果。6. 上线后陆续踩过的几个坑以及对应的优化方案上线几个月后集群平稳运行过也陆续出现过一些不算致命但很影响体验的问题。这些经验比搭建过程更值钱单独列一节讲清楚。6.1 从库复制延迟从 100 秒降到 3 秒的调整TB 级数据、读写分离后读从库很容易被大查询拖慢进而导致Seconds_Behind_Master居高不下。我一直觉得一主三从里最需要监控的不是主库负载而是从库的复制延迟。一次大促前巡检我发现其中一台从库延迟已经到 100 秒再下去读写分离的意义就没了。排查后原因有两层。一是 MySQL 5.7 的默认配置里从库复制是单线程的SQL 线程一个事务一个事务地往前推遇到大事务或 DDL 就卡住。我在从库上加了并行复制参数[mysqld] slave_parallel_type LOGICAL_CLOCK slave_parallel_workers 8 slave_preserve_commit_order ON注意 5.7 的并行复制只有在binlog_format ROW且主库开启了组提交的情况下才有比较明显的效果。另外slave_preserve_commit_order保证了并行回放时不会因为事务顺序错乱导致数据不一致这个参数不能省。其次是读写分离带来的读放大问题。大量的分析型查询直接打到同一台从库上把它的 CPU 打满了复制线程吃不到资源。解决方法是把查询量最大的几个报表接口单独分到指定从库其他常规读走另外两台从库之间做按业务维度的分流而不是完全随机负载均衡。调整完以后三台从库的延迟长期稳定在 3 秒以内。6.2 半同步复制要不要上MySQL 5.7 默认的复制是异步的主库事务提交成功就返回binlog 还没送到从库。这种模式在主库宕机时理论上可能丢失最后一个事务。半同步复制能压缩这个窗口但也带来额外的性能开销。我在实际环境中是分阶段开的先只在主库和两台关键从库之间开启半同步观察了大半个月确认没有明显写入性能劣化后才在剩下那台上同步开启。半同步配置需要注意参数[mysqld] plugin-load semisync_master.so;semisync_slave.so rpl_semi_sync_master_enabled 1 rpl_semi_sync_slave_enabled 1 rpl_semi_sync_master_timeout 1000 rpl_semi_sync_master_wait_point AFTER_SYNCrpl_semi_sync_master_timeout设置成 1000 毫秒很关键。如果从库确认延迟超过这个值主库会自动退化为异步复制至少保证写入不会一直卡死。AFTER_SYNC是比默认值更安全的模式它保证在事务提交到存储引擎之前就等待从库确认这样即使主库马上宕机事务也已经在从库的 relay log 里了。网上很多文章只提开启半同步不提AFTER_SYNC在这个点上容易被带偏。不过要提醒你半同步不能保证零丢失。主库已经提交但还没拿到从库确认的那个瞬间如果主库挂了事务仍然可能丢失。所以它只能作为降低丢数据概率的手段不能替代定期的完整备份和 binlog 备份。6.3 日常巡检、备份和监控切换做得再好数据也可能在某个角落出问题。我现在的日常运维节奏是每周用 XtraBackup 做一次全量备份保留最近四周的备份集每天做一次增量备份保留最近七天。巡检时用orchestrator -c topology -alias cluster看拓扑是否一致用SHOW SLAVE STATUS看延迟重点看有没有NO状态。数据一致性用pt-table-checksum分库分表跑限速参数开到很低避免给线上带来压力。发现差异后用pt-table-sync修复修复前必须先备份相关表结构。监控指标主要盯主从延迟、从库复制进程状态、主库磁盘空间、ProxySQL 后端健康状态、连接池使用率、慢查询数量。这套方案跑到现在最让我放心的不是“主库挂了自动切”而是每次切换后都知道流量会往哪里走旧主恢复后不会再被写入从库会重新对齐到新主。这种确定性才是高可用方案真正该追求的东西。最后再分享一个小技巧所有和 Orchestrator 挂钩的切换脚本一定要写成幂等的。也就是说同一个脚本连续执行两次要么第二次不产生新影响要么能安全地纠正掉第一次的残留异常。我的脚本里在UPDATE mysql_servers之前加了判断如果目标 IP 已经存在就不再重复执行这样即使 Orchestrator 因为异常重复触发同一个恢复流程ProxySQL 的配置也不会被写烂。这些小细节看着不起眼但关键时刻能少挨几次半夜的告警电话。
返回列表