MySQL 主从复制:binlog、relay log、复制线程与并行复制(125)

MySQL与关系型数据库约 7 分钟阅读3,895 字符
知识库文档 126 / 251

阅读时间:约 45 分钟 | 难度:中高级 | 编号:125


一、引言:主从复制是高可用的基石

MySQL 主从复制是最基础也是最重要的高可用方案。几乎所有的高可用架构——读写分离、数据备份、故障切换——都建立在主从复制之上。

主从复制的核心: 主库将变更写入 binlog,从库通过 I/O 线程拉取 binlog 写入 relay log,SQL 线程重放 relay log 中的事件。

文章配图
文章配图


二、主从复制架构

2.1 三个线程

线程 位置 职责
Binlog Dump 线程 主库 发送 binlog 给从库
I/O 线程 从库 拉取 binlog 写入 relay log
SQL 线程 从库 读取 relay log 重放事件

2.2 复制流程

code
主库: 事务提交 → 写 binlog → Binlog Dump 线程发送
从库: I/O 线程接收 → 写 relay log → SQL 线程重放

2.3 复制模式

模式 说明 适用场景
异步复制 主库提交后不等待从库 默认,性能最好
半同步复制 主库等待至少一个从库确认收到 binlog 减少数据丢失
全同步复制 主库等待所有从库应用完成 极少使用

文章配图
文章配图


三、binlog 与 relay log

3.1 binlog

  • 主库产生,记录所有数据变更
  • 格式:STATEMENT / ROW / MIXED
  • 文件:binlog.000001, binlog.000002...

3.2 relay log

  • 从库产生,I/O 线程从主库接收 binlog 后写入
  • 格式与 binlog 相同
  • 文件:relay-log.000001, relay-log.000002...
  • SQL 线程重放后自动删除

3.3 关键位置

概念 说明
Master_Log_File + Master_Log_Pos 主库 binlog 位置(I/O 线程读取到哪)
Relay_Master_Log_File + Exec_Master_Log_Pos SQL 线程执行到主库 binlog 的哪个位置

文章配图
文章配图


四、复制格式(ROW vs STATEMENT)

4.1 STATEMENT 格式

  • 记录 SQL 语句
  • 日志小,网络传输快
  • 缺点:不确定函数(NOW(), UUID())导致主从不一致

4.2 ROW 格式(默认)

  • 记录每行数据的变化
  • 精确,保证主从一致
  • 缺点:日志大,网络传输慢

4.3 MIXED 格式

  • 默认 STATEMENT,需要时自动切换为 ROW

文章配图
文章配图


五、并行复制

5.1 为什么需要并行复制

传统复制只有一个 SQL 线程,主库多线程并发写入,从库单线程串行回放,延迟不可避免。

并行复制: 多个 SQL 线程同时回放 relay log 中的事务。

5.2 MySQL 5.6:基于 Schema 的并行复制

按数据库(Schema)并行。不同数据库的事务可以并行回放,同一个数据库的事务串行。

5.3 MySQL 5.7:基于 Group Commit 的并行复制(LOGICAL_CLOCK)

主库上同一组提交的事务在从库上可以并行回放。基于 binlog 的 last_committed 和 sequence_number。

5.4 MySQL 8.0:基于 Write Set 的并行复制

主库记录事务修改的行,如果两个事务修改的行没有冲突,可以在从库并行回放。比 LOGICAL_CLOCK 更细粒度。

sql
-- 查看并行复制配置
SHOW VARIABLES LIKE 'slave_parallel_workers';
SHOW VARIABLES LIKE 'binlog_transaction_dependency_tracking';

文章配图
文章配图


六、复制延迟

6.1 延迟的原因

原因 说明
主库并发高 从库单线程回放慢
大事务 从库回放大事务耗时
网络延迟 跨机房复制
从库性能差 从库硬件不如主库

6.2 监控延迟

sql
-- 查看从库延迟(秒)
SHOW SLAVE STATUS\G
-- Seconds_Behind_Master: 0  ← 延迟秒数

-- 查看并行复制状态
SELECT * FROM performance_schema.replication_applier_status_by_worker;

6.3 减少延迟

  1. 开启并行复制(slave_parallel_workers > 0)
  2. 使用 ROW 格式 + WRITESET 依赖跟踪
  3. 拆分大事务
  4. 从库硬件不低于主库

文章配图
文章配图


七、GTID 复制

7.1 什么是 GTID

GTID(Global Transaction Identifier)是事务的全局唯一标识。

code
GTID = server_uuid:transaction_id
例如: 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-100

7.2 GTID 的优势

  • 简化主从切换:不需要手动指定 binlog 位置
  • 自动跳过已执行的事务
  • 便于故障恢复

7.3 配置 GTID

sql
-- 主库和从库都需要配置
gtid_mode = ON
enforce_gtid_consistency = ON

文章配图
文章配图


八、总结

组件 作用
binlog 主库变更日志
relay log 从库接收的 binlog 副本
I/O 线程 从库拉取 binlog
SQL 线程 从库回放 relay log
并行复制 多线程回放,减少延迟
GTID 全局事务标识,简化切换

文章配图
文章配图


九、面试官视角提问

Q1:主库突然宕机,从库提升为主库。如何确保没有数据丢失?半同步复制能保证零丢失吗?

Q2:**Seconds_Behind_Master**为 0,但主从数据不一致。可能的原因是什么?如何排查?

Q3:MySQL 5.7 的 LOGICAL_CLOCK 和 MySQL 8.0 的 WRITESET 并行复制有什么区别?哪种更高效?

Q4:从库的 SQL 线程停止了,但 I/O 线程正常运行。从库的 relay log 会无限增长吗?如何恢复?

Q5:一个表没有主键,对主从复制有什么影响?为什么 ROW 格式下没有主键的表会导致从库延迟?

Q6:GTID 复制下,如果从库手动插入了一条数据(不应该这么做),然后主库也插入了相同的数据,会发生什么?


作者注:本文为系列第 125 篇,属于「08 MySQL 与关系型数据库」分类。


十、面试官提问 · 参考答案

Q1:主库宕机,从库提升为主库。如何确保零数据丢失?半同步能保证吗?

参考答案:

半同步复制不能保证零丢失,只能减少丢失窗口。

半同步复制: 主库等待至少一个从库确认收到 binlog 后才提交。但如果主库在等待确认时宕机,已提交但未确认的事务在主库上已提交,从库可能没有。

零丢失方案: Group Replication(MGR)或 InnoDB Cluster。MGR 使用 Paxos 协议,多数派确认后才提交,可以保证零丢失。

增强半同步:rpl_semi_sync_master_wait_for_slave_count=1 且 rpl_semi_sync_master_wait_point=AFTER_SYNC 可以最大程度减少丢失。


Q2: 但主从数据不一致,可能原因?

参考答案:

可能原因:

  1. 从库直接写入:有人在从库上手动执行了 INSERT/UPDATE/DELETE
  2. STATEMENT 格式 binlog:不确定函数(NOW(), UUID())导致主从计算结果不同
  3. 主库使用了**sql_log_bin=0**:某些操作不记录 binlog,从库无法同步
  4. 从库复制过滤:replicate_ignore_table 等过滤规则跳过了某些表
  5. Bug:MySQL 版本的复制 bug

排查: 使用 pt-table-checksum 检查主从数据一致性,pt-table-sync 修复不一致。


Q3:MySQL 5.7 LOGICAL_CLOCK 和 8.0 WRITESET 并行复制的区别?

参考答案:

维度 5.7 LOGICAL_CLOCK 8.0 WRITESET
并行粒度 同一组提交的事务 不冲突的事务
依赖检测 基于 group commit 基于 Write Set(修改的行)
并行度 较低(依赖主库提交顺序) 较高(不冲突即可并行)
性能 好 更好

WRITESET 更高效: 因为两个事务即使在不同组提交,只要修改的行不冲突,就可以并行回放。


Q4:从库 SQL 线程停止,I/O 线程正常。relay log 会无限增长吗?

参考答案:

会无限增长,直到磁盘满。 I/O 线程继续从主库拉取 binlog 写入 relay log,但 SQL 线程停止不会清理已执行完的 relay log。

恢复步骤:

  1. 查看 SQL 线程停止原因:SHOW SLAVE STATUS\G 看 Last_SQL_Error
  2. 修复错误(如主键冲突、表不存在)
  3. 重启 SQL 线程:START SLAVE SQL_THREAD
  4. 或跳过错误:SET GLOBAL sql_slave_skip_counter = 1; START SLAVE;

Q5:没有主键的表对主从复制有什么影响?

参考答案:

ROW 格式下,没有主键的表会导致从库严重延迟。

原因: ROW 格式下,UPDATE/DELETE 通过主键定位行。没有主键时,InnoDB 使用隐藏的 DB_ROW_ID 定位,但需要全表扫描来找到匹配的行。主库上 1ms 的 UPDATE,从库上可能需要 1 秒。

最佳实践: 所有表必须有显式主键。DBA 应该定期检查并告警无主键表。


Q6:GTID 下从库手动插入数据,然后主库也插入相同数据,会发生什么?

参考答案:

从库的复制会停止,报错 GTID 冲突或主键冲突。

场景一: 从库插入 id=100,主库也插入 id=100。主库 binlog 传到从库,从库重放时发现主键冲突,SQL 线程停止。

场景二: 从库插入的记录主键不冲突,但从库的 GTID 不是主库产生的。如果主库后来执行了相同 GTID 的事务,从库会认为该 GTID 已执行而跳过。

最佳实践: 从库设置为 read_only=ON(除了复制线程),防止误操作。


全文完 | 编号 125 | 分类 08 MySQL 与关系型数据库