主从复制与高可用 · 复制原理
MySQL 主从复制的整体流程?(binlog dump、IO thread、relay log、SQL thread)
The overall process of MySQL master-slave replication? (binlog dump, IO thread, relay
答案语言
考察要点
这道题面试官想看你是否深入理解 MySQL 异步复制的完整数据流和核心组件。考察点包括:
- 核心流程的掌握:你是否能清晰描述从 Master 产生变更到 Slave 应用变更的每一步,并准确说出
binlog、log dump 线程、I/O 线程、relay log、SQL 线程各自的角色和交互关系。 - 关键机制的理解:你是否明白复制过程中的关键设计,如
relay log的缓冲作用、GTID 的优势、半同步复制的权衡。 - 问题定位与优化能力:你是否知道主从延迟(Replication Lag)产生的原因,并能提出有效的监控和优化方案,如并行复制(MTS)。
核心概念
MySQL 主从复制是一个异步过程,核心目标是让从库(Slave/Replica)的数据与主库(Master/Source)保持最终一致。其标准流程涉及三大线程和两种日志。
整体流程图示
mermaid
1graph TD2 subgraph Master (主库)3 A[1. Client执行DML/DDL] --> B{Storage Engine};4 B -- 提交事务 --> C[2. 写入Binlog];5 D[3. Log Dump 线程] -- 读取Binlog --> E((Network));6 end78 subgraph Slave (从库)9 F[4. I/O 线程] -- 请求并接收Binlog --> E;10 F --> G[5. 写入Relay Log];11 H[6. SQL 线程] -- 读取Relay Log --> I[7. 重放(Replay)SQL];12 I --> J{Storage Engine};13 end1415 style Master fill:#f9f,stroke:#333,stroke-width:2px16 style Slave fill:#ccf,stroke:#333,stroke-width:2px
核心组件详解
-
二进制日志 (Binary Log /
Binlog)- 作用:主库上以二进制格式记录所有修改数据库数据的事件(DML、DDL)。它是复制的基础。
- 格式 (
binlog_format):- STATEMENT:记录原始的 SQL 语句。优点是日志量小,缺点是某些非确定性函数(如
NOW(),UUID())或LIMIT无ORDER BY的语句可能导致主从数据不一致。 - ROW:记录每一行数据的变更前(before-image)和变更后(after-image)的值。优点是能保证数据绝对一致,缺点是日志量较大,尤其在
UPDATE或DELETE影响大量行时。MySQL 5.7.7+ 版本后,ROW 格式为默认格式,因为它在数据一致性上更可靠。 - MIXED:混合模式。MySQL 会根据 SQL 语句的类型自动选择 STATEMENT 或 ROW 格式。
- STATEMENT:记录原始的 SQL 语句。优点是日志量小,缺点是某些非确定性函数(如
-
Log Dump 线程
- 位置:主库。
- 作用:当从库连接主库时,主库会为该从库创建一个 Log Dump 线程。该线程负责读取主库的
Binlog事件,并根据从库 I/O 线程请求的坐标(Binlog文件名 + Position)发送给从库。
-
I/O 线程 (Receiver Thread)
- 位置:从库。
- 作用:负责与主库建立连接,向主库的 Log Dump 线程请求指定位置之后的
Binlog。接收到Binlog事件后,将其顺序写入到从库本地的Relay Log(中继日志)中。
-
中继日志 (Relay Log)
- 位置:从库。
- 作用:本质上是 I/O 线程从主库拉取回来的
Binlog的一个本地副本。它的存在实现了读写分离:I/O 线程负责网络数据接收和磁盘写入,SQL 线程负责从本地磁盘读取和执行。这避免了 SQL 线程的执行速度受网络波动的影响,也减轻了主库的压力(Binlog只需被拉取一次)。
-
SQL 线程 (Applier Thread)
- 位置:从库。
- 作用:读取 Relay Log 中的事件,并将其在从库上重放(Apply),从而实现数据的同步。在单线程复制模式下,SQL 线程是串行执行的,这也是造成主从延迟的主要瓶颈。
-
状态记录文件/表
master.info(MySQL 8.0.22 前) /mysql.slave_master_info表:记录从库连接主库所需的信息(主库地址、端口、用户名、密码)以及 I/O 线程当前读取到的主库Binlog的坐标(文件名和位置)。relay-log.info(MySQL 8.0.22 前) /mysql.slave_relay_log_info表:记录 SQL 线程当前已经执行完的 Relay Log 的坐标。- 版本差异:在老版本中,这些信息存储在文件中,存在 crash-unsafe 的风险。MySQL 5.6+ 引入了
master_info_repository=TABLE和relay_log_info_repository=TABLE参数,允许将这些状态信息存储在InnoDB表中,利用事务特性保证了 crash-safe。MySQL 8.0+ 默认使用表存储。
常见低分回答
- 流程混淆:将 I/O 线程和 SQL 线程的功能搞混,例如说“I/O 线程负责执行 SQL”,或者完全忽略 Relay Log,描述成“I/O 线程直接从主库读取 SQL 并执行”。
- 忽略
Binlog格式:当被问到复制可能出现的问题时,想不到 STATEMENT 格式下的非确定性函数会导致数据不一致,这表明对Binlog的理解不够深入。 - 对延迟原因的理解过于肤浅:只回答“网络慢”或“机器性能差”,而没有点出核心瓶颈——单线程的 SQL 线程在处理主库并发写入时,由于需要串行回放,必然会产生延迟。
- 描述过于简单:“主库把数据同步给从库”,这种回答没有任何技术含量,无法体现专业性。需要明确说出线程、日志、数据流转的细节。
- 不知道 GTID:在讨论故障转移或多级复制时,如果不知道 GTID (Global Transaction ID),说明知识体系还停留在比较旧的 MySQL 版本,无法应对现代高可用架构的讨论。
工程实践与优化
-
主从延迟(Replication Lag)监控与定位
- 监控:通过
SHOW SLAVE STATUS\G命令查看Seconds_Behind_Master的值。注意:这个值在某些情况下可能不准(例如网络抖动)。更精确的监控可以使用 Percona 的pt-heartbeat工具,它通过在主库插入一个时间戳表,然后在从库查询该表来计算真实延迟。 - 定位:延迟通常发生在 SQL 线程。如果
Exec_Master_Log_Pos与Read_Master_Log_Pos(通过SHOW SLAVE STATUS查看)差距很大,说明 I/O 线程正常,但 SQL 线程执行慢。可以通过SHOW PROCESSLIST查看从库上是否有慢查询或大事务正在执行。
- 监控:通过
-
并行复制(Multi-Threaded Slave, MTS)
- 目的:解决单 SQL 线程瓶颈,提升从库应用 Relay Log 的速度。
- MySQL 5.6:引入了基于
DATABASE的并行复制,即不同库的事务可以并行应用。但如果压力集中在单一数据库,效果不佳。 - MySQL 5.7:引入了更强大的基于
LOGICAL_CLOCK的并行复制。主库在生成Binlog时,会为可以并行执行的事务打上相同的last_committed值。从库的coordinator线程根据这个值将事务分发给多个worker线程并行执行,大大提升了复制性能。通过设置slave_parallel_workers > 0和slave_parallel_type = LOGICAL_CLOCK开启。 - MySQL 8.0:进一步增强,引入了
WRITESET模式。它基于事务修改的行(主键)来判断事务是否可以并行,即使是同一 schema 下的事务,只要不修改相同的行,就可以并行执行,并行度更高。
-
半同步复制(Semi-Synchronous Replication)
- 目的:在异步复制和全同步复制之间取得平衡,提升数据一致性。
- 流程:主库提交事务后,不会立即返回给客户端,而是等待至少一个从库确认已接收到该事务的
Binlog事件并写入 Relay Log 后,才返回成功。 - 权衡:
- 优点:在主库发生宕机时,能保证至少有一个从库拥有最新的数据,为无损故障转移提供了可能。
- 缺点:增加了主库事务的响应时间(RTT)。如果从库响应慢或网络故障,主库会被阻塞,直到超时(
rpl_semi_sync_master_timeout)后自动降级为异步复制。
- 版本差异:MySQL 5.7 引入了
AFTER_SYNC模式,相比之前的AFTER_COMMIT,它在主库InnoDB引擎提交前就等待从库 ACK,进一步增强了数据一致性。
-
GTID(全局事务标识符)
- 格式:
source_id:transaction_id,在整个复制集群中唯一。 - 优势:
- 简化故障转移:当需要将从库指向新主库时,不再需要手动查找和设置
Binlog文件和位置。只需CHANGE MASTER TO ... MASTER_AUTO_POSITION=1,从库会自动从断点处开始复制。 - 避免重复执行:从库会记录已执行的 GTID 集合,即使接收到重复的事务,也会自动跳过。
- 高可用架构基石:MHA、Orchestrator、
InnoDBCluster 等高可用方案都强依赖 GTID。
- 简化故障转移:当需要将从库指向新主库时,不再需要手动查找和设置
- 格式:
常见追问与延伸题目
-
追问:如果
Seconds_Behind_Master持续增长,你如何排查和解决?- 回答要点 1 (排查):
- 首先,执行
SHOW SLAVE STATUS\G,确认是Slave_IO_Running还是Slave_SQL_Running为No。如果是,先解决线程启动问题。 - 如果都为
Yes,比较Relay_Log_Space是否持续增长,以及Exec_Master_Log_Pos和 I/O 线程读取的Relay_Master_Log_File+Exec_Master_Log_Pos之间的差距。差距大说明 SQL 线程是瓶颈。 - 在从库上执行
SHOW PROCESSLIST,查看 SQL 线程是否正在执行一个耗时很长的 DDL 或大事务。 - 检查从库的系统资源,如 CPU、I/O 是否有瓶颈。
- 首先,执行
- 回答要点 2 (解决):
- 短期:如果是一个大 DDL 阻塞,考虑在业务低峰期执行。如果是大事务,考虑在主库侧拆分。
- 长期:
- 开启并行复制:这是最有效的手段。在 MySQL 5.7+ 中,设置
slave_parallel_workers为一个合理的值(如 4-16),并使用LOGICAL_CLOCK。 - 优化查询:在主库上导致慢的查询,在从库上回放同样会慢。确保所有写操作都有合适的索引。
- 硬件升级:为从库配置更快的磁盘(SSD)和更强的 CPU。
- 架构调整:如果写压力实在太大,考虑分库分表,从根本上降低单机写 QPS。
- 开启并行复制:这是最有效的手段。在 MySQL 5.7+ 中,设置
- 回答要点 1 (排查):
-
追问:半同步复制能保证数据完全不丢失吗?为什么?
- 回答要点:不能 100% 保证。半同步复制只保证了在主库返回
COMMIT成功给客户端之前,至少有一个从库接收到了Binlog并写入了 Relay Log。但存在一个极端情况:- 主库事务 T1 提交。
- Log Dump 线程将 T1 的
Binlog发送给从库。 - 从库接收成功,写入 Relay Log,并发送 ACK 给主库。
- 主库接收到 ACK,准备在存储引擎层提交事务 T1 并返回给客户端。
- 就在这一瞬间,主库宕机了。
- 此时,客户端没有收到成功的响应,可能会重试事务。但从库上已经有了这个事务的记录。如果此时进行主从切换,新的主库(原从库)就比旧的主库多了一个事务,造成了数据不一致。MySQL 5.7 的
loss-less semi-sync改进了这一点,但理论上的微小窗口依然存在。
- 回答要点:不能 100% 保证。半同步复制只保证了在主库返回
-
追问:如果我想搭建一个一主多从的结构,其中一个从库专门用于报表分析,不希望它因为主库的 DDL 操作(如
ALTER TABLE)而长时间阻塞,该怎么办?- 回答要点 1 (延迟复制):可以配置这个报表从库为延迟复制。通过
CHANGE MASTER TO ... MASTER_DELAY = N(N 为秒数),让它故意延迟 N 秒应用日志。这样,如果主库执行了一个错误的 DDL 或 DML,你有 N 秒的时间在主库上修复,并在它影响到报表库之前停止复制。 - 回答要点 2 (过滤复制):可以使用复制过滤规则(如
replicate-do-db,replicate-ignore-table等)让报表从库只复制它关心的库或表,忽略其他不必要的变更。对于 DDL,可以考虑使用pt-online-schema-change或gh-ost这类工具在主库上进行在线 DDL,它们将 DDL 转化为对影子表的 DML 操作,对从库的影响更平滑。 - 回答要点 3 (逻辑备库):更彻底的方案是,不使用物理复制,而是通过 Canal、Maxwell 等工具订阅主库的
Binlog,解析成结构化数据后写入专门用于分析的数据仓库(如 ClickHouse, Hive, Doris),实现真正的读写分离和异构数据同步。
- 回答要点 1 (延迟复制):可以配置这个报表从库为延迟复制。通过