MySQL 主从复制是数据库高可用与读写分离的基石:主库接收写入,将变更记录到 binlog(二进制日志),从库拉取 binlog 并重放,实现数据同步。这条链路看似简单,但涉及三种日志格式、两类线程、以及「异步复制的延迟问题」这个永恒话题。本篇讲清复制的完整机制。
复制的链路:三个环节的接力
环节一:主库写 binlog。每当事务提交,MySQL 将变更事件写入 binlog——binlog 是逻辑日志(记录的是 SQL 语句或行变更,不是物理页),有三种格式:STATEMENT——记录原始 SQL 语句(文件小,但非确定性函数如 NOW()/UUID() 在主从可能产生不同结果);ROW——记录每一行的实际变更(数据一致性最好,但批量操作产生大量日志,本库语境的「逐行记录」);MIXED——默认用 STATEMENT,不安全的语句自动切换为 ROW。生产推荐 ROW 格式:一致性最有保障,配合 binlog_row_image = minimal 可以减少日志量。
环节二:从库的 IO 线程拉取 binlog。从库启动一个 IO 线程,连接主库,请求从某个 binlog 位点开始发送变更事件——主库的 binlog dump 线程推送事件,从库 IO 线程接收并写入本地的 relay log(中继日志——主库 binlog 的本地副本)。relay log 的意义:将从库「拉取」与「重放」解耦——IO 线程只负责拉(快),SQL 线程只负责重放(慢),两者通过 relay log 做缓冲。
环节三:从库的 SQL 线程重放 relay log。SQL 线程读取 relay log 中的事件,在从库上执行——重放完成后,从库的数据与主库一致。单线程重放是复制延迟的主要原因(主库多线程写入,从库单线程重放——写入速度 > 重放速度时延迟持续增长;MySQL 5.7+ 的并行复制通过按库/按组提交并行重放缓解了这个问题)。
GTID:从「文件名 + 偏移量」到「全局事务 ID」
传统复制的痛点:主从之间的位点同步依赖于binlog 文件名 + 偏移量(如 mysql-bin.000003:1234)——主库切换(主从故障转移)时,新主的 binlog 文件名和偏移量与旧主完全不同,从库需要手动计算新的位点,操作复杂且容易出错。GTID(Global Transaction Identifier)的解法:给每个事务分配一个全局唯一的 ID(server_uuid:transaction_id),从库只需告诉主库「我已经执行到 GTID XXX」,主库自动从正确的位置开始发送——主从切换时位点自动对齐,运维成本大幅降低。GTID 是 MySQL 5.6+ 的功能,生产环境强烈建议开启。
复制延迟:异步复制的宿命与半同步的折中
MySQL 默认的复制是异步的——主库提交事务后不等待从库确认即返回客户端成功——主库宕机时,最近的事务可能尚未同步到从库,造成数据丢失。半同步复制(semi-sync replication)的折中方案:主库等待至少一个从库确认收到 binlog(ACK)后才返回成功——不要求从库执行完成(只要求「收到了日志」),在安全性与性能之间取得平衡。复制的延迟监控是运维的日常:Seconds_Behind_Master 是从库视角的延迟秒数(0 表示已追平),但它有一个陷阱——主库无写入时该值为 0(不代表「追平了」,只是「没有新变更来衡量延迟」),更可靠的指标是对比主从的 GTID 差异。
GTID 的格式与自动定位机制
GTID 的格式是 server_uuid 加冒号加 transaction_id(如 3E11FA47:23),其中 server_uuid 是 MySQL 实例的唯一标识,transaction_id 是该实例上事务的序号。GTID 集合是一组 GTID 的范围表示,用于表示已执行的事务集合。自动定位机制:从库连接主库时发送自己的 GTID 集合,主库对比自己的 GTID 集合,自动从从库缺失的事务开始发送——不需要手动指定 binlog 文件名与偏移量。GTID 的限制:GTID 事务必须是原子的(不支持非事务性存储引擎的操作)。GTID 的运维收益:主从切换时 CHANGE MASTER TO 命令从指定 binlog 文件与偏移量变为 MASTER_AUTO_POSITION = 1——一行命令完成位点对齐。
主从切换的实操:从手动到半自动
主库宕机时的主从切换(failover)是 MySQL 运维的关键操作。手动切换的步骤:① 确认主库确实不可用(避免脑裂——两台都认为自己是主);② 从库执行 STOP SLAVE 停止复制;③ 从库执行 RESET SLAVE ALL 清除复制配置;④ 从库开启读写(SET GLOBAL read_only = 0);⑤ 应用切换数据库连接到新主。每一步都可能出错——这就是为什么生产环境使用自动故障转移工具(Orchestrator、MHA、MySQL InnoDB Cluster)。自动工具的优势:故障检测更准确(避免脑裂)、位点对齐更精确(GTID 自动计算)、切换时间更短(秒级 vs 手动的分钟级)。切换后的数据一致性检查是必要步骤:使用 pt-table-checksum 对比主从数据的一致性——发现不一致时用 pt-table-sync 修复。
并行复制的演进:从库级到组提交
MySQL 复制延迟的根本原因是 SQL 线程的单线程重放(主库多线程写入但从库单线程重放),MySQL 的并行复制经历了三个版本的演进。MySQL 5.6 的库级并行:按数据库(schema)分组并行重放——不同库的事务可以并行,同一库的事务串行——对于单库大事务的场景效果有限。MySQL 5.7 的组提交并行(LOGICAL_CLOCK):基于 binlog 的组提交信息——同一组提交的事务(在主库上并行执行的事务)在从库上也可以并行重放——并行度取决于主库的写入并发度。MySQL 8.0 的基于 WRITESET 的并行:利用事务的写集(事务修改的行的哈希集合)判断事务之间的冲突——不冲突的事务可以并行,即使它们不在同一个组提交中——并行度最高。生产环境的建议:升级到 MySQL 8.0 并开启 WRITESET 并行复制(transaction_write_set_extraction = XXHASH64),是从库追平延迟的最有效手段。
主从切换的实操与自动故障转移
主库宕机时的主从切换是关键操作。手动切换步骤:确认主库不可用避免脑裂、从库停止复制、清除复制配置、开启读写、应用切换连接。每一步都可能出错——生产环境使用自动故障转移工具如 Orchestrator 或 MHA 或 MySQL InnoDB Cluster。自动工具的优势:故障检测更准确避免脑裂、位点对齐更精确(GTID 自动计算)、切换时间更短。切换后的数据一致性检查是必要步骤:使用 pt-table-checksum 对比主从数据的一致性。
并行复制的演进:从库级到组提交到 WRITESET
MySQL 复制延迟的根本原因是 SQL 线程的单线程重放。并行复制经历三个版本演进。MySQL 5.6 的库级并行:按数据库分组并行重放——不同库的事务可以并行。MySQL 5.7 的组提交并行:基于 binlog 的组提交信息——同一组提交的事务在从库也可以并行重放。MySQL 8.0 的 WRITESET 并行:利用事务的写集判断事务之间的冲突——不冲突的事务可以并行,即使不在同一个组提交中——并行度最高。生产环境建议升级到 MySQL 8.0 并开启 WRITESET 并行复制,是从库追平延迟的最有效手段。
binlog 的三种格式对比
binlog 的格式决定了日志记录的是什么以及复制的一致性保证。STATEMENT 格式记录原始 SQL 语句——文件最小但非确定性函数如 NOW 或 UUID 在主从可能产生不同结果。ROW 格式记录每一行的实际变更——一致性最好但日志量大,binlog_row_image 等于 minimal 可以减少日志量。MIXED 格式默认用 STATEMENT,不安全的语句自动切换为 ROW。ROW 格式的额外好处是 binlog 可以被解析为结构化的变更事件——Canal 或 Debezium 类 CDC 工具订阅 binlog 做数据同步——ROW 格式提供了精确的行级变更信息。生产推荐 ROW 格式。
复制的监控指标与告警
MySQL 复制的监控有关键指标需要告警。Seconds_Behind_Master:从库落后主库的秒数——持续大于 0 说明复制延迟。Slave_IO_Running:IO 线程是否运行。Slave_SQL_Running:SQL 线程是否运行。Last_IO_Errno 与 Last_IO_Error:IO 线程的最近错误。告警的设置:Seconds_Behind_Master 大于 60 持续 5 分钟告警;Slave_IO_Running 为 No 立即告警;SQL 线程报错立即告警。监控的采集方式:通过 SHOW SLAVE STATUS 获取所有复制状态指标,接入 Prometheus 的 mysqld_exporter 自动采集。
复制的监控指标与告警体系
MySQL 复制的监控有关键指标。Seconds_Behind_Master 从库落后主库的秒数持续大于零说明延迟。Slave_IO_Running 与 Slave_SQL_Running 是两个线程的状态。Last_IO_Errno 与 Last_SQL_Errno 是最近的错误代码。告警设置:Seconds_Behind_Master 大于 60 持续 5 分钟告警;IO 或 SQL 线程为 No 立即告警。监控通过 SHOW SLAVE STATUS 获取接入 Prometheus 的 mysqld_exporter。
MySQL 复制的监控与告警体系
MySQL 复制的监控需要覆盖多个维度。延迟监控通过 Seconds_Behind_Master 指标持续大于 60 秒告警。线程状态监控 Slave_IO_Running 和 Slave_SQL_Running 均为 Yes 才正常。错误监控 Last_IO_Errno 和 Last_SQL_Errno 非零时告警。数据一致性校验定期使用 pt-table-checksum 工具对比主从数据。GTID 差异监控对比主从的 Executed_Gtid_Set 判断延迟。告警接入 Prometheus 的 mysqld_exporter 自动采集。复制的监控是数据库运维的核心环节,延迟的增长意味着从库数据不一致,故障转移时可能丢数据。
小结
MySQL 主从复制收成四条:复制的链路是三环节接力——主库写 binlog(推荐 ROW 格式)、从库 IO 线程拉取到 relay log、SQL 线程重放(单线程重放是延迟的主因,并行复制缓解);GTID 替代「文件名 + 偏移量」——从库只需报告 GTID 即可自动对齐位点,主从切换的运维成本大幅降低;异步复制是延迟与丢失的根源——半同步复制用「等一个从库 ACK」换安全性;复制的监控不能只看 Seconds_Behind_Master——GTID 差异是更可靠的延迟指标。主从复制是数据库高可用的第一课——它的每一条设计决策(异步/半同步、格式选择、GTID),都是在一致性、可用性与性能之间的权衡。
参考资料
- MySQL 官方文档:Replication —— 复制机制与配置的权威说明
- 本库前篇:《InnoDB 中的事务隔离级别和锁的关系》《缓存一致性实战》
- 《高性能 MySQL》第 10 章—— 复制机制与延迟优化的经典讲解
读者留言
COMMENTS 暂无还没有留言,来说第一句?