MySQL主从复制原理详解

文章导读
搜「MySQL主从复制原理」的人,多半不是想读一遍教科书,而是遇到延迟、数据不一致或者从库连不上,想先确认:现在这条复制链路走到哪一步了、断的是哪一环。最直接的动作是先看从库的复制状态,再回到主库的 binlog 位置做对照,而不是一上来就翻配置文件。判断入口通常就是 SHOW REPLICA STATUS\G(旧版本为 SHOW SLAVE STATUS\G)里的 Replica_IO_Runn
📋 目录
  1. 壹 MySQL主从复制原理和实现
  2. 贰 MySQL主从复制原理和步骤
  3. 叁 MySQL主从复制:先把能核对的事实摆清楚
  4. 肆 MySQL主从复制最容易搞错的一点
  5. 伍 MySQL主从复制:一个照着就能做完的做法
A A

搜「MySQL主从复制原理」的人,多半不是想读一遍教科书,而是遇到延迟、数据不一致或者从库连不上,想先确认:现在这条复制链路走到哪一步了、断的是哪一环。最直接的动作是先看从库的复制状态,再回到主库的 binlog 位置做对照,而不是一上来就翻配置文件。判断入口通常就是 SHOW REPLICA STATUS\G(旧版本为 SHOW SLAVE STATUS\G)里的 Replica_IO_Running、Replica_SQL_Running、Seconds_Behind_Source、Last_Error 这几个字段。

MySQL 主从复制的判断顺序建议固定为:先看从库 IO 线程是否能把主库 binlog 拉过来,再看 SQL 线程是否把 relay log 回放完,最后才去比对两边数据。字段名和命令在不同版本有差异,MySQL 8.0.22 起 SHOW SLAVE STATUS 逐步换名为 SHOW REPLICA STATUS,具体以你实例上 SELECT VERSION(); 的输出为准。线程状态只说明链路通不通,不说明数据一致,两者要分开判断。

MySQL主从复制原理和实现

「原理和实现」对应的是链路本身怎么搭起来:主库把变更写进 binlog,从库的 IO 线程连到主库拉取 binlog 写入本地 relay log,SQL 线程再按 relay log 顺序回放。确认这一步是否成立,不是看配置写了什么,而是看线程状态。

在从库执行:

SHOW REPLICA STATUS\G

重点读这几行:Replica_IO_Running: Yes 表示 IO 线程正常连上主库;Replica_SQL_Running: Yes 表示回放线程在跑;Source_Host、Source_Log_File、Read_Source_Log_Pos 是它当前认的主库地址和位点。同时回主库看一眼:

SHOW MASTER STATUS\G

输出里的 File 和 Position 是主库当前写入位置。如果从库的 Read_Source_Log_Pos 长期落后于主库的 Position,说明 IO 线程在拉但没追上,问题在网络、主库 binlog 保留或大事务,而不是配置写错。

MySQL主从复制原理和步骤

「原理和步骤」对应的是从零建立复制的操作链路,每一步都有可核对的落点。建议按下面顺序确认,每步都有一个能看的地方:

MySQL主从复制原理详解
  1. 主库开启 binlog:执行 SHOW VARIABLES LIKE 'log_bin';,Value 应为 ON;同时看 SHOW VARIABLES LIKE 'server_id';,主从的 server_id 不能相同。
  2. 主库建复制账号:在 mysql.user 里能查到对应账号,权限字段包含 Repl_slave_priv(8.0 里显示为 Repl_slave_priv 列)。
  3. 记录主库位点:即上一步 SHOW MASTER STATUS 输出的 File 与 Position。
  4. 从库执行 CHANGE REPLICATION SOURCE TO ...(8.0.23 起)或 CHANGE MASTER TO ...(更早版本),把主库地址、账号、位点填进去。
  5. 从库 START REPLICA;,再回到 SHOW REPLICA STATUS\G 看两个线程是否都为 Yes。

每步该看到什么很明确:第 1 步看变量值,第 2 步看账号权限,第 3 步看位点数字,第 5 步看两个 Running 字段。Last_IO_Error 和 Last_SQL_Error 为空,才算这一步走完。

MySQL主从复制:先把能核对的事实摆清楚

在判断原因之前,先把对象和前提固定下来,避免把不同层面的问题混在一起。

对象层面,要确认三件事:主库和从库各自的 SELECT VERSION(); 输出;主库 SHOW VARIABLES LIKE 'binlog_format'; 的值(通常是 ROW);从库 SHOW VARIABLES LIKE 'server_id'; 是否唯一。

现象层面,能核对的只有这些位置:从库 SHOW REPLICA STATUS\G 里的线程状态与错误字段;从库错误日志(一般位于 datadir 下,文件名形如 *.err,路径可由 SHOW VARIABLES LIKE 'log_error'; 确认)里的连接失败或回放报错;主库 SHOW PROCESSLIST; 里是否能看到来自从库 IP 的 Binlog Dump 线程;主库 SHOW BINARY LOGS; 里从库正在读的那个文件是否还在。

这一步只摆事实、不下结论。把「线程断没断」「位点差多少」「binlog 还在不在」三件事分别写下来,再进入判断。

MySQL主从复制最容易搞错的一点

最常见的误解是把「两个线程都是 Yes」等同于「数据已经一致」。这两件事没有必然关系:线程状态只说明 IO 在拉、SQL 在回放,不保证回放过的事件和主库当前数据一致,也不保证从库没有被人为写入过。

MySQL主从复制原理详解

区分两种情况的检查动作是比对校验值。可以先在主库和从库分别执行:

CHECKSUM TABLE 库名.表名;

把主从两侧的 Checksum 结果对照。若线程为 Yes 但校验值不同,问题在数据层(可能是从库被写入、回放中断过、或者建复制时位点填错);若线程为 No 且 Last_Error 有内容,问题在链路层。这两种情况处理方式完全不同,先分清再动手。

MySQL主从复制:一个照着就能做完的做法

下面是一条从确认到建链的完整路径,参数按实际情况替换,每一步都标注该看到的结果。

-- 1. 主库:确认 binlog 开启、server_id 唯一
SHOW VARIABLES LIKE 'log_bin';
SHOW VARIABLES LIKE 'server_id';
SHOW VARIABLES LIKE 'binlog_format';
-- 期望:log_bin=ON,binlog_format 通常为 ROW

-- 2. 主库:创建复制账号(IP 段按实际替换)
CREATE USER 'repl'@'10.0.0.%' IDENTIFIED BY '替换为强密码';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.0.%';
FLUSH PRIVILEGES;

-- 3. 主库:记录位点,这里输出的 File 和 Position 要抄下来
SHOW MASTER STATUS\G

-- 4. 从库:先停掉已有复制(新建可跳过)
STOP REPLICA;

-- 5. 从库:指向主库并填入第 3 步的位点
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='10.0.0.10',
  SOURCE_PORT=3306,
  SOURCE_USER='repl',
  SOURCE_PASSWORD='替换为强密码',
  SOURCE_LOG_FILE='mysql-bin.000001',
  SOURCE_LOG_POS=154;
-- MySQL 8.0.23 之前使用 CHANGE MASTER TO,参数名对应为 MASTER_HOST 等

-- 6. 从库:启动复制
START REPLICA;

-- 7. 从库:核对结果
SHOW REPLICA STATUS\G

第 7 步要看到 Replica_IO_Running: Yes、Replica_SQL_Running: Yes、Last_IO_Error 与 Last_SQL_Error 为空。Seconds_Behind_Source 在刚启动时可能不为 0,需要给它回放的时间,不能立刻当作延迟故障。如果 IO 线程为 Connecting,先回到主库用 SHOW PROCESSLIST; 看有没有来自从库的连接,再从从库错误日志里找具体报错,通常是账号、网络或主库 bind-address 的限制。

建链过程中还要注意一点:如果主库上已有历史数据,必须先做一次全量备份导入从库,再从备份对应的位点开始复制,否则从库会缺这段数据,后续校验对不上。全量备份的位点一般记录在备份文件里,具体位置取决于所用备份工具,需要结合工具文档确认,不能凭印象填。