数据库基础体系 · 第 18/139 篇。文章以各产品官方稳定版本的公开语义为准;示例会明确引擎、事务与部署边界。
MySQL 复制与高可用:Binlog、GTID、半同步、切换与一致性
一、先明确问题:复制解决什么,不能解决什么
MySQL 主从复制(MySQL 8.x 官方文档逐渐使用 source/replica,传统资料中的 master/slave 对应 source/replica)是把源服务器上的数据库变更传送并重放到一个或多个副本服务器。
它通常用于:
- 读扩展;
- 备份和报表读取;
- 故障切换;
- 跨机房数据复制;
- 降低单台服务器故障对业务的影响。
但复制本身不是共识协议,也不是自动高可用系统。异步复制下,源库已经提交的事务可能尚未到达副本;半同步复制可以缩小这个窗口,但不能保证副本已经执行事务,也不能自动完成选主、流量切换和旧源隔离。
因此需要区分四个问题:
- 变更如何记录:Binlog。
- 副本如何定位和去重:GTID。
- 源库何时认为提交可以返回:异步或半同步复制。
- 故障后谁成为新源,以及客户端读到什么:切换流程和一致性控制。
二、Binlog:复制的数据来源
2.1 Binlog 与 InnoDB Redo Log 不是一回事
MySQL 中常见的两类日志职责不同:
| 日志 | 主要用途 | 内容形态 |
|---|---|---|
| InnoDB Redo Log | 崩溃恢复,保证已提交修改能够恢复 | 更接近物理页或存储引擎内部变更 |
| Binary Log(Binlog) | 复制、时间点恢复、审计式变更追踪 | 面向逻辑操作和事务事件 |
例如:
CREATE TABLE account (
id BIGINT PRIMARY KEY,
balance DECIMAL(12, 2) NOT NULL
) ENGINE = InnoDB;
START TRANSACTION;
UPDATE account
SET balance = balance - 100
WHERE id = 1;
UPDATE account
SET balance = balance + 100
WHERE id = 2;
COMMIT;
InnoDB 可能会修改多个数据页和索引页,并把这些修改写入 Redo Log。Binlog 则记录这次事务对应的逻辑事件:
- 事务开始或事务边界;
- 两条
UPDATE的语句事件,或者对应的行变更事件; - 事务提交事件;
- GTID 等复制元数据。
副本并不接收源库的 InnoDB 数据页,而是读取 Binlog,再由副本自己的 InnoDB 执行或应用这些变更。
因此:
- Redo Log 不能直接替代 Binlog 做复制;
- Binlog 也不能替代 Redo Log 做本机崩溃恢复;
- 备份文件加上 Binlog,才能构造从某个时间点继续恢复的能力。
2.2 Binlog 的基本数据流
一个事务从源库到副本,通常经过以下路径:
客户端
│
│ 事务提交
▼
源库 InnoDB ──写入 Binlog──> 源库 Binlog 文件
│
│ 复制连接
▼
副本 I/O 接收线程 ──写入 Relay Log──> 副本 SQL/applier 线程
│
▼
副本 InnoDB
副本至少包含两个逻辑阶段:
- 接收阶段:副本连接源库,读取 Binlog,写入本地 Relay Log。
- 应用阶段:副本从 Relay Log 读取事件,在本地执行事务。
所以“副本已经收到”不等于“副本已经执行”。
可以用 SHOW REPLICA STATUS\G 检查状态。不同版本和配置下字段很多,核心字段包括:
Replica_IO_Running:接收线程是否运行;Replica_SQL_Running:应用线程是否运行;Source_Log_File、Read_Source_Log_Pos:已经从源库读取到哪里;Relay_Source_Log_File、Exec_Source_Log_Pos:已经应用到源库 Binlog 的哪里;Seconds_Behind_Source:一个近似延迟指标;Last_IO_Error、Last_SQL_Error:接收或应用失败原因;Retrieved_Gtid_Set:已经接收到的 GTID 集合;Executed_Gtid_Set:已经在副本执行的 GTID 集合。
Seconds_Behind_Source 不能作为唯一的一致性判断依据。它可能因为源库无新事务、事务长时间执行、时钟差异或并行应用而失真。GTID 集合和具体事务的等待结果通常更可靠。
2.3 Binlog 格式:STATEMENT、ROW 和 MIXED
Binlog 的主要格式有:
STATEMENT:记录执行过的 SQL 语句;ROW:记录行变化前后的必要信息;MIXED:根据语句类型在两种格式之间选择。
生产环境通常优先使用 ROW,因为它较少依赖副本上的环境和执行结果。例如:
UPDATE orders
SET status = 'expired'
WHERE created_at < NOW() - INTERVAL 30 DAY;
如果使用语句格式,NOW()、随机数、非确定性函数、不同 SQL_MODE、不同字符集设置等都可能使副本得到不同结果。行格式直接描述被修改的行,复制确定性更好。
但行格式也有边界:
- 大批量更新会产生大量行事件;
binlog_row_image=FULL会记录完整行,日志量更大;MINIMAL可以减少部分日志量,但要求表结构、主键和副本状态满足复制需要;- 没有合适主键的表可能导致副本定位和应用效率变差;
- DDL 仍然需要在副本上执行相应的元数据变更。
因此,“使用 ROW 就绝对不会不一致”是错误的。副本上的表结构、过滤规则、手工写入、存储引擎和版本差异仍可能造成问题。
2.4 Binlog 保留与清理
复制依赖源库上的 Binlog。若副本停机太久,源库清理了副本尚未读取的日志,副本就无法从原位置继续复制。
常见恢复路径有两种:
- 从仍然保留的 Binlog 继续;
- 使用新的备份重新初始化副本,再开启 GTID 自动定位。
不要仅凭磁盘空间不足就直接删除 Binlog。应先确认:
- 所有副本已经读取并应用到需要的位置;
- 备份和时间点恢复策略仍然成立;
- 没有延迟副本、审计订阅者或其他复制通道依赖这些日志。
Binlog 的删除只影响源库上的历史日志文件,不会自动修复已经落后的副本。
三、事务提交与复制:异步为什么会丢数据
3.1 异步复制的提交路径
异步复制下,源库提交事务时不等待副本确认。简化后的过程是:
- 客户端向源库执行事务;
- InnoDB 写入事务修改;
- 源库写入并提交 Binlog;
- 源库向客户端返回成功;
- 副本稍后读取 Binlog;
- 副本再应用事务。
因此,在第 4 步之后、第 5 步之前,源库可能已经故障。
设:
- :源库已经向客户端确认成功的事务集合;
- :副本已经收到并写入 Relay Log 的事务集合;
- :副本已经执行完成的事务集合。
通常有:
异步复制允许这三个集合之间存在差距。发生源库永久损坏时,至少可能丢失:
这就是复制层面的潜在 RPO。RPO(Recovery Point Objective)表示故障后允许丢失的数据范围。
3.2 持久化设置仍然重要
即使使用复制,源库本地事务的持久性仍受到存储配置影响。常见的强持久性组合是:
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
其直觉是:
innodb_flush_log_at_trx_commit=1:每次事务提交时将 InnoDB Log Buffer 中的日志写入并刷新到持久存储;sync_binlog=1:每次事务提交相关的 Binlog 写入都进行同步。
这不是绝对保证。硬件缓存、磁盘控制器、电源保护、虚拟化存储和文件系统行为仍会影响真正的持久性。降低这些参数可以改善吞吐,但扩大崩溃时可能丢失最近事务的窗口。
此外,Redo Log 和 Binlog 之间需要满足事务提交的一致性。MySQL 使用两阶段提交协调 InnoDB 与 Binlog:
- InnoDB 进入 prepare 状态;
- Binlog 写入并按配置同步;
- InnoDB 完成 commit;
- 崩溃恢复时依据两者状态决定提交或回滚。
这解决的是源库自身的崩溃一致性,并不意味着副本已经应用事务。
四、GTID:用事务身份代替文件位置
4.1 GTID 的结构
GTID(Global Transaction Identifier)是事务的全局标识,常见形式为:
source_server_uuid:transaction_number
例如:
3e11fa47-71ca-11e1-9e33-c80aa9429562:42
其中:
source_server_uuid标识产生该事务的服务器;transaction_number是该服务器分配的递增编号。
多个 GTID 可以表示为 GTID 集合,例如:
3e11fa47-71ca-11e1-9e33-c80aa9429562:1-42
表示同一 UUID 下编号 1 到 42 的事务。
GTID 的核心价值不是“编号更好看”,而是让副本能够回答:
这个事务我是否已经执行过?
在传统文件位置复制中,副本需要知道:
mysql-bin.000123:456789
但主库切换后,文件名和位置可能完全不同。GTID 则允许副本根据已经执行的事务集合自动寻找缺失事务。
4.2 执行集合、清理集合与自动跳过
副本维护的 Executed_Gtid_Set 可以抽象为集合 。源库当前可提供的 Binlog 事务集合记为 。
副本需要的事务大致是:
如果某个事务的 GTID 已经在副本的执行集合中,副本在复制过程中可以跳过该事务,而不是再次执行。
这对故障切换非常重要。例如:
源库已产生:
A:1, A:2, A:3, A:4
副本已执行:
A:1, A:2, A:3
新源只需要继续提供 A:4。如果副本已经执行 A:4,即使复制连接重新从包含 A:4 的日志范围开始,也不会把同一事务再次执行成两次。
但 GTID 不会自动解决所有冲突:
- 如果两个服务器各自产生了不同事务,可能形成分叉;
- 如果有人绕过复制直接在副本写入,可能产生不兼容事务;
- 如果
server_uuid重复,GTID 身份会被破坏; - 如果使用复制过滤器,GTID 集合存在不代表所有数据库对象都一致。
4.3 GTID 自动定位示例
源库和副本都应先正确配置 GTID 相关参数,并保证 server_uuid 唯一。一个概念性的配置示例:
# 源库和副本都需要
gtid_mode = ON
enforce_gtid_consistency = ON
log_bin = ON
log_replica_updates = ON
log_replica_updates=ON 的作用是让副本把自己从上游接收到并执行的事务继续写入自己的 Binlog。这对级联复制和故障切换后的继续复制很重要。
在副本上配置:
CHANGE REPLICATION SOURCE TO
SOURCE_HOST = 'source.example.com',
SOURCE_PORT = 3306,
SOURCE_USER = 'repl',
SOURCE_PASSWORD = 'change_me',
SOURCE_AUTO_POSITION = 1;
START REPLICA;
实际生产中不应把明文密码直接留在命令历史中;应使用权限受控的凭据管理方式,并限制复制账号权限。
验证:
SHOW REPLICA STATUS\G
重点观察:
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Auto_Position: 1
Last_IO_Error:
Last_SQL_Error:
Executed_Gtid_Set: ...
SOURCE_AUTO_POSITION=1 表示使用 GTID 自动定位,而不是指定 SOURCE_LOG_FILE 和 SOURCE_LOG_POS。
4.4 GTID 模式的迁移边界
不能简单地把在线生产环境的 gtid_mode 直接从 OFF 改成 ON,然后期待现有复制自动安全转换。GTID 模式迁移需要遵循版本支持的分阶段流程,逐步处理:
enforce_gtid_consistency;gtid_mode的过渡状态;- 现有事务和复制通道;
- 所有副本的兼容配置。
迁移前应验证是否存在不能生成 GTID 的语句或事务模式。具体过渡步骤应以目标 MySQL 8.4 版本文档为准,而不是套用旧版本博客中的参数变更顺序。
五、半同步复制:缩小确认成功后的丢失窗口
5.1 半同步的定义
半同步复制介于异步和同步之间:
- 异步:源库不等待副本确认;
- 半同步:源库至少等待规定数量的副本确认;
- 强同步或共识式复制:通常需要更严格的多数派和成员状态协议。
MySQL 半同步复制由源库端和副本端插件共同参与。MySQL 8.4 中相关系统变量使用 rpl_semi_sync_source_* 和 rpl_semi_sync_replica_* 命名。
一个典型配置方向是:
# 源库
rpl_semi_sync_source_enabled = ON
rpl_semi_sync_source_timeout = 1000
rpl_semi_sync_source_wait_for_replica_count = 1
# 副本
rpl_semi_sync_replica_enabled = ON
实际启用前需要确认对应插件已安装并加载,变量名称和动态配置能力以当前 MySQL 8.4 安装包及文档为准。
5.2 一次半同步提交发生了什么
假设源库配置要求至少一个副本确认:
- 客户端向源库提交事务;
- 源库把事务写入 Binlog;
- 源库等待副本确认;
- 副本接收事务并写入 Relay Log;
- 副本发送确认;
- 源库完成等待点之后的提交流程;
- 源库向客户端返回成功。
关键在第 4 步:半同步确认通常表示副本已经收到事务并按半同步协议写入 Relay Log,而不是已经在副本 InnoDB 中执行完成。
因此:
但通常不能推出:
5.3 AFTER_SYNC 与 AFTER_COMMIT
半同步等待点会影响故障语义。常见等待点包括:
AFTER_SYNC:源库将 Binlog 同步后,在完成最终提交前等待副本确认;AFTER_COMMIT:源库完成本地提交后,再等待副本确认。
AFTER_SYNC 通常具有更好的提交顺序和故障语义,但具体表现仍取决于版本、插件和存储持久化配置。不要仅凭“启用了半同步”就推断所有故障场景都不会丢数据。
5.4 超时后通常会退化为异步
半同步不是无限等待协议。超过:
rpl_semi_sync_source_timeout = 1000
指定的等待时间后,源库通常会退化为异步方式继续处理,避免一个失联副本阻塞整个写入系统。
这意味着:
运维上必须监控:
- 当前半同步是否实际生效;
- 等待次数和超时次数;
- 已连接且可确认的副本数量;
- 复制延迟;
- 源库是否长期处于异步退化状态。
半同步解决的是“源库确认成功后,数据至少到达一个副本”的部分问题,不解决:
- 自动选主;
- 旧源隔离;
- 客户端连接切换;
- 副本应用延迟;
- 双主写入冲突;
- 备份损坏或误删数据。
六、复制一致性:收到、执行、可读是三个状态
6.1 三种常被混淆的一致性
对一个事务来说,至少需要区分:
- 源库提交成功:源库向客户端返回成功;
- 副本接收成功:事务进入副本 Relay Log;
- 副本执行成功:事务已经应用到副本 InnoDB,查询可以看到结果。
半同步主要覆盖第 2 种;读副本要求的是第 3 种。
例如客户端执行:
INSERT INTO orders(id, status)
VALUES (1001, 'paid');
随后立刻把请求路由到副本:
SELECT status
FROM orders
WHERE id = 1001;
异步复制下可能返回空结果;半同步复制下也仍可能返回空结果,因为半同步确认不必等待副本 SQL/applier 线程执行完成。
6.2 用 GTID 实现读后写一致性
如果客户端在源库执行事务后能获得该事务所属的 GTID,就可以让后续读请求等待副本执行到这个 GTID。
示意:
-- 在写入连接或应用侧获得本次事务对应的 GTID 集合
-- 例如:'3e11fa47-71ca-11e1-9e33-c80aa9429562:42'
-- 在读副本上执行
SELECT WAIT_FOR_EXECUTED_GTID_SET(
'3e11fa47-71ca-11e1-9e33-c80aa9429562:42',
5
);
返回值通常可按以下方式理解:
0:副本已经执行到目标 GTID;1:等待超时;NULL:参数或执行出现错误。
应用应在返回 0 后再读取业务数据;超时则应:
- 转回源库读取;
- 返回明确的重试或降级结果;
- 或根据业务接受短暂旧读。
这个机制只解决因复制延迟造成的读后写问题,不解决事务被回滚、业务读写路由错误或数据已经分叉的问题。
6.3 事务隔离级别与复制可见性
副本执行完成后,查询是否立即看到结果,还受到事务隔离级别影响。
例如,客户端在副本上开启一个长事务:
START TRANSACTION;
SELECT * FROM orders WHERE id = 1001;
即使复制线程随后执行了插入,当前事务的 InnoDB 一致性读仍可能根据 Read View 看不到这个新版本。再次执行普通 SELECT 也不一定看到新数据,直到事务结束并重新建立新的读视图。
所以:
复制已应用
不等于:
当前已有事务一定可见
这与 MySQL 事务和锁中的 Read View、当前读、间隙锁语义是两个层次:
- 复制线程决定副本物理数据何时被应用;
- InnoDB 隔离级别决定某个事务何时能观察到这些版本。
SELECT ... FOR UPDATE 等当前读还会涉及锁竞争,可能让复制应用变慢,进而扩大复制延迟。
6.4 并行复制会改变完成顺序
副本可以使用多个应用线程并行执行不冲突的事务。于是:
- 接收顺序通常仍按源库 Binlog 顺序;
- 应用过程可能并行;
- 对无依赖事务,完成时间可能不同;
- 对存在依赖的事务,副本需要保持必要的提交顺序或依赖关系。
因此,不能把“某个 SQL 线程当前没有报错”理解成“所有已接收事务都已经完成”。判断具体事务是否可读,应使用 GTID 等待或等价的事务位点检查。
七、切换与故障转移:先保护一致性,再改变角色
7.1 计划内切换(switchover)
计划内切换的目标是尽量做到零或接近零数据丢失。一个安全流程需要包含以下逻辑步骤。
第一步:停止或冻结新写入
先通过应用层、代理层或数据库权限控制阻止新的写事务进入旧源库。不能只执行:
SET GLOBAL read_only = ON;
就认为已经完全阻止写入,因为具有足够权限的账号、复制线程或特定管理操作可能不受普通 read_only 限制。生产环境通常还会配合:
SET GLOBAL super_read_only = ON;
并在连接入口实施流量隔离。
第二步:等待副本追平
在副本上检查:
SHOW REPLICA STATUS\G
确认:
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Last_IO_Error:
Last_SQL_Error:
更严格的方式是获得旧源库最后一批事务的 GTID 集合,然后在候选副本上等待:
SELECT WAIT_FOR_EXECUTED_GTID_SET(
'source_uuid:1-10500',
30
);
返回 0 才表示该副本已经执行到目标集合。
第三步:隔离旧源库
停止旧源库继续接受写入,必要时停止数据库服务或切断其业务网络。这个动作叫 fencing,即隔离旧角色,防止它在切换后仍然被客户端写入。
只改变 DNS、VIP 或代理后端,不一定足够,因为:
- DNS 有缓存;
- 连接池可能保留旧连接;
- 长连接可能继续使用旧源;
- 旧源可能仍被某个应用实例直接访问。
第四步:提升候选副本
在已追平的副本上停止复制,并解除只读限制:
STOP REPLICA;
SET GLOBAL read_only = OFF;
SET GLOBAL super_read_only = OFF;
是否执行 RESET REPLICA、RESET REPLICA ALL,要根据后续拓扑决定。它们可能清理复制元数据或连接配置,不应作为“提升副本”的无条件步骤。
第五步:切换客户端入口
更新代理、VIP、服务发现或应用配置,使新连接进入新源。切换后验证:
- 新源可写;
- 旧源不可写;
- 新源包含切换前最后事务;
- 应用连接池已刷新;
- 监控看到新的复制拓扑。
计划内切换的核心条件可以形式化为:
如果这个条件不成立,切换即使看起来成功,也可能已经丢失最近事务。
7.2 非计划故障转移(failover)
旧源已经不可用时,通常不能等待它提供完整的最后 GTID 集合。此时要在多个副本中选择执行进度最靠前的候选者。
假设:
副本 R1: A:1-100
副本 R2: A:1-103
副本 R3: A:1-101
在确认没有其他来源的事务集合后,R2 的数据进度最靠前。它仍可能丢失源库已经提交但尚未传播的 A:104 及后续事务,但相比其他候选者更接近旧源状态。
故障转移至少有三个风险:
风险一:已确认事务丢失
如果事务已经返回给客户端,但没有到达任何可用副本,该事务可能无法恢复。半同步只能降低这种概率;如果发生超时退化或所有确认副本同时故障,仍可能丢失。
风险二:旧源“复活”造成双写
如果网络分区后旧源重新恢复,但它不知道自己已被提升的副本替代,应用仍可能向旧源写入。这会形成两个写入分支,也称 split-brain。
必须先隔离旧源,或者使用外部仲裁、STONITH(通过电源或管理接口强制关闭旧节点)等机制确保旧源不能继续提供写服务。
风险三:副本之间存在分叉
如果某个副本被人工写入,或者旧源曾经作为独立源产生了新 GTID,新源和旧源可能各自包含对方没有的事务。此时不能靠:
RESET MASTER;
或删除日志“抹平”问题。应该:
- 保存故障现场和日志;
- 比较各节点 GTID 集合;
- 识别哪些事务只存在于某一分支;
- 判断业务上如何合并或舍弃;
- 必要时从可信节点重新构建副本。
数据分叉是数据恢复问题,不是复制线程重启问题。
八、复制失败的诊断路径
8.1 接收线程停止
表现:
Replica_IO_Running: No
Last_IO_Error: ...
常见原因:
- 网络不可达;
- 复制账号认证失败;
- TLS 配置不匹配;
- 源库 Binlog 已被清理;
- 源库未开启 Binlog;
server_uuid或复制元数据配置错误。
处理顺序应是:
- 读取完整
Last_IO_Error; - 验证 DNS、端口和网络;
- 验证账号、权限和证书;
- 检查源库 Binlog 是否仍包含需要的位置;
- 不要直接执行
RESET REPLICA ALL破坏现场。
8.2 应用线程停止
表现:
Replica_IO_Running: Yes
Replica_SQL_Running: No
Last_SQL_Error: ...
这意味着副本可能继续接收日志,但无法应用。常见原因:
- 主键冲突;
- 表不存在或结构不一致;
- 复制过滤器造成依赖缺失;
- DDL 与 DML 并发导致元数据锁等待或失败;
- 副本被人工写入;
- 存储引擎、字符集或 SQL_MODE 差异;
- 大事务或锁冲突导致长时间停顿。
不要默认使用:
SET GLOBAL sql_replica_skip_counter = 1;
START REPLICA;
跳过事务可能让复制状态恢复为“运行”,但数据状态已经缺失。只有在明确知道该事务影响范围、能够校验数据并记录修复方案时,才可以采用跳过或注入补偿事务。GTID 模式下也不能通过随意修改 gtid_next 来掩盖数据问题。
8.3 延迟高但线程都在运行
“线程为 Yes”只表示没有立即报错,不表示复制没有延迟。应继续观察:
Retrieved_Gtid_Set与Executed_Gtid_Set的差距;- 单个事务是否很大;
- 副本是否存在长事务和锁等待;
- 磁盘 I/O、CPU、Redo Log、Relay Log 是否饱和;
- 并行应用线程是否不足;
- 源库是否发生大批量 DDL 或更新。
源库上的长事务会持有锁、产生大量 Binlog,并可能让副本在应用时遇到同样的锁竞争。复制延迟有时不是网络问题,而是副本执行路径上的 InnoDB 锁和 I/O 问题。
九、备份、复制和高可用的边界
9.1 复制不是备份
复制会复制很多逻辑错误:
DROP TABLE important_data;
如果该语句进入 Binlog,副本也可能执行删除。误删、错误更新、应用 Bug 会被快速传播到所有副本。
复制也不能替代:
- 全量备份;
- 增量或 Binlog 归档;
- 时间点恢复;
- 备份恢复演练;
- 跨故障域保存的副本。
一个副本可以用于备份,但备份过程必须考虑:
- 备份时的一致性;
- 长事务和锁影响;
- Binlog 位点或 GTID 记录;
- 备份文件是否可恢复;
- 恢复后是否能继续应用对应 Binlog。
9.2 只读副本也不是绝对只读
副本通常配置:
read_only = ON
super_read_only = ON
但仍需限制管理账号和应用账号权限,避免人为写入。副本上的直接写入可能导致:
- 主键冲突;
- GTID 分叉;
- 后续复制中断;
- 切换时无法判断哪个事务可信。
9.3 MySQL 复制与更强的高可用协议
传统异步或半同步复制主要是 source-to-replica 日志传输。它缺少完整的成员管理和自动仲裁。
如果需要自动成员检测、故障判定和组内一致的主节点管理,可以研究 MySQL Group Replication 及基于它的 InnoDB Cluster 等方案。但这并不意味着所有问题消失:
- 网络分区仍需处理;
- 事务冲突仍有代价;
- 节点数量和多数派影响可用性;
- 客户端路由仍需正确配置;
- 备份仍然必需。
高可用系统的最小闭环不是“部署两个 MySQL”,而是:
数据复制
+ 故障检测
+ 选主或人工决策
+ 旧主隔离
+ 客户端路由
+ 一致性验证
+ 可恢复备份
十、一个可验证的切换判断模型
可以把一次切换前的状态表示为:
- :旧源库停止写入时已经提交的 GTID 集合;
- :候选副本已经执行的 GTID 集合;
- :候选副本已经接收到但尚未执行的 GTID 集合;
- :候选副本缺失、但旧源可能已经拥有的 GTID 集合。
则:
其中三者不一定严格互斥,实际判断应以 MySQL 的 GTID 集合运算和复制状态为准。计划内切换要求:
也就是候选副本没有缺失旧源已提交事务。若:
就必须在“继续等待”“接受数据丢失”“换另一个候选副本”之间做出明确决策,而不是仅看 Seconds_Behind_Source = 0。
故障转移时,旧源不可访问,通常只能在候选副本之间比较:
选择集合最完整且数据可信的副本,同时记录无法恢复的 GTID 范围。这些 GTID 范围代表故障时可能丢失的事务边界,应当进入故障报告和业务核对流程。
结语
Binlog 是复制和时间点恢复的变更载体;GTID 为事务提供可追踪、可去重、可自动定位的身份;半同步复制把源库确认成功与副本接收之间的距离缩小,但通常不等于副本已经执行;切换则必须同时处理事务追平、旧源隔离、客户端路由和新源验证。
最容易出错的判断有三个:
-
“半同步开启,所以副本一定已经可读。”
不成立,半同步确认通常早于副本应用完成。 -
“两个复制线程都是 Yes,所以数据一致。”
不成立,可能存在延迟、过滤、结构差异或尚未执行的事务。 -
“把备用库提升为主库就完成高可用。”
不成立,没有 fencing、流量切换、GTID 验证和恢复方案,切换可能造成双写或数据丢失。
真正可靠的 MySQL 高可用设计,必须把日志持久化、事务身份、复制确认、InnoDB 可见性、故障路径和恢复验证放在同一个因果链中考察。
系列导航与关联阅读
- 系列入口:数据库完整学习路线:从关系模型、事务索引到分布式与向量检索
- 上一篇:MySQL 查询优化:EXPLAIN、统计信息、Join、排序和慢日志
- 下一篇:MySQL 生产运维:参数、容量、备份、监控与常见故障排查
- 延伸:MySQL 事务与锁:Read View、间隙锁、死锁和一致性读
官方资料
本文依据数据库官方文档重新梳理;正文、示例与生产检查清单由 WR BLOG 编写。

评论
0 条讨论