人人都会AI编程

19.1 主从复制核心原理:Binlog Dump、IO 线程、SQL 线程

更新时间:2026-07-11

MySQL 主从复制是构建高可用架构、读写分离和数据备份的基础。它的核心思想并不复杂:让一台服务器(从库)持续同步另一台服务器(主库)上发生的所有数据变更。理解其原理,能帮助你在发生主从延迟或中断时快速定位问题,而不会面对一片茫然。

19.1.1 整体复制流程

主从复制的运作可以浓缩为三个步骤,对应三个线程:

  1. 主库上,当有数据变更(如 INSERT、UPDATE、DELETE)时,这些变更会被记录到二进制日志(Binlog)中。
  2. 从库启动复制后,会有一个 IO 线程通过常规客户端连接协议,向主库请求 Binlog 事件。主库为每个从库连接启动一个 Binlog Dump 线程,负责读取本地的 Binlog 并发送给从库。
  3. 从库的 IO 线程收到这些事件后,先写入本地的中继日志(Relay Log)。然后从库的 SQL 线程读取 Relay Log 中的事件,在本地重放执行,最终实现与主库数据的一致。

这三个线程——主库的 Binlog Dump 线程、从库的 IO 线程、从库的 SQL 线程——构成了复制的核心骨架,缺一不可。

19.1.2 Binlog 与 Binlog Dump 线程

Binlog(二进制日志)是主从复制的唯二数据来源(另一个是 GTID,后面会介绍)。它记录了数据库上所有可能引起数据变更的 SQL 语句(基于语句的复制)或受影响行的实际数据变更(基于行的复制)。Binlog 是物理日志吗?不,它存储的是逻辑的更新事件,而不是物理页的变更,这使得 MySQL 可以在不同版本、甚至不同操作系统的服务器之间进行复制。

在复制过程中,主库并不主动“推”数据给从库,而是等待从库连接并请求。当从库的 IO 线程发出请求时,主库会为这个连接分配一个专门的 Binlog Dump 线程。这个线程会:

  • 首先检查从库请求的 Binlog 文件名和位置(或 GTID 集合)。
  • 然后顺序读取本地的 Binlog 文件,将符合条件的事件逐个发送给从库。读取是顺序 I/O,效率极高。
  • 一旦当前的文件读完,如果还有后续事件写入,Dump 线程会等待并持续发送,保持一个“准实时”的推送流。
  • 如果从库断连后重连,Dump 线程可以继续从上一次断开的位置(或 GTID)开始发送,不会重复或遗漏。

你可以通过 SHOW PROCESSLIST 在主库上看到正在为从库服务的 Dump 线程,它的 Command 通常显示为 Binlog Dump。这个线程是主库上唯一主动为复制服务的组件,它对主库的正常读写几乎没有影响。

19.1.3 从库的 IO 线程

从库上的 IO 线程负责连接主库,接收 Binlog 事件并写入 Relay Log。它的工作流程是:

  1. 根据 CHANGE MASTER TO 指定的连接信息,使用一个普通的 MySQL 客户端连接主库。
  2. 发送一个“Binlog Dump”命令给主库,附带当前从库已经接收到的 Binlog 位置(如 mysql-bin.000001,位置 120)或 GTID 集合。
  3. 接收主库 Dump 线程不断推送过来的事件流。
  4. 将这些事件顺序写入本地的 Relay Log 文件。

IO 线程有几个关键行为需要注意:

  • 它是独立工作的,不会因为 SQL 线程的执行快慢而阻塞,两者是异步的。因此,从库上 IO 线程和 SQL 线程可以有不同的进度。
  • 如果网络中断或主库重启,IO 线程会检测到连接断开,并自动尝试重连(默认重连间隔由 MASTER_RETRY_COUNTMASTER_CONNECT_RETRY 控制)。
  • IO 线程会定期更新本地的 master.info 文件(或表 mysql.slave_master_info),记录当前读取的 Binlog 文件名和位置,以便下次启动时能继续。

你可以用 SHOW SLAVE STATUS 查看 IO 线程的状态,其中 Slave_IO_Running 显示为 Yes 表示 IO 线程正常,Master_Log_FileRead_Master_Log_Pos 会告诉你目前从主库拉取到了哪个位置。

19.1.4 从库的 SQL 线程

SQL 线程是真正在从库上重放数据变更的线程。它从本地的 Relay Log 中读取事件,然后像普通客户端一样执行这些事件对应的 SQL 或行变更。

这个线程有几个值得关注的特性:

  • 串行执行(默认):从库的 SQL 线程只有一个,它严格按照 Relay Log 中事件的顺序逐一执行。这意味着主库上即使并发执行的事务,到了从库也会被串行化重放。这也是传统复制容易产生延迟的根本原因——如果主库写并发很高,从库单线程回放可能跟不上。
  • 可以开启并行复制:MySQL 5.6 开始引入了基于 schema 的并行复制,5.7 引入了基于组提交的并行复制(LOGICAL_CLOCK),8.0 进一步优化。通过调整 slave_parallel_workers 参数,你可以让从库使用多个工作线程并行执行 Relay Log 中的事务,大幅降低延迟。这是解决主从延迟最直接有效的手段之一。
  • 错误处理:SQL 线程执行某条语句出错时(比如从库上意外多删了一条数据导致复制失败),默认会停止运行,将 Slave_SQL_Running 设为 No。你可以根据错误号选择跳过该错误继续(sql_slave_skip_counter),但必须谨慎,避免数据不一致。

通过 SHOW SLAVE STATUSSlave_SQL_Running 显示 SQL 线程是否运行,Relay_Log_FileExec_Master_Log_Pos 告诉你已经执行到主库的哪个 Binlog 位置。对比 IO 线程的读取位置,你可以计算出主从延迟到底卡在传输还是执行上。

19.1.5 一个具体的复制启动例子

假设你配置好一台从库,并执行:

CHANGE MASTER TO
  MASTER_HOST='192.168.1.100',
  MASTER_USER='repl',
  MASTER_PASSWORD='pass',
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=154;
START SLAVE;

在这一刻:

  1. 从库的 IO 线程连接主库,发送请求:“请从 mysql-bin.000001 的 154 位置开始给我发事件”。
  2. 主库的 Dump 线程准备好最新的 Binlog 文件,从那个位置开始顺序发送 INSERT/UPDATE/DELETE 等事件。
  3. 从库 IO 线程收到事件后,写入 Relay Log,并记录当前已经收到 154+收到的长度(比如 520)。
  4. 从库 SQL 线程读 Relay Log,将收到的事件一条条在本地执行,同时更新已执行的位置。
  5. 当主库有新的数据变更时,Dump 线程会持续推送,形成实时的同步流。

如果中途从库网络中断,IO 线程会报错,SQL 线程消耗完 Relay Log 中已有的事件后暂停。网络恢复后,IO 线程重新连接,从故障前记录的位置继续拉取,不会丢失任何已确认接收的事件。

19.1.6 监控与实用命令

日常运维中,你需要依靠几个命令快速掌握主从状态:

  • SHOW MASTER STATUS:查看主库当前正在写入的 Binlog 文件和位置。
  • SHOW SLAVE STATUS\G:查看从库 IO 和 SQL 线程状态、日志文件、位置、延迟秒数(Seconds_Behind_Master)、错误信息等。
  • SHOW PROCESSLIST:可以看到主库上的 Binlog Dump 线程和从库上的 IO/SQL 线程。
  • SELECT * FROM performance_schema.replication_connection_status 等,用于更细致的复制监控。

掌握了这三个线程的分工协作,你就接触到了 MySQL 复制最底层的“骨架”。后续任何高可用、读写分离、数据一致性处理方案,都建立在这个简单而可靠的设计之上。