Skip to content

主从复制

MySQL 主从复制用来把数据的写入变更从一台机器(主库)传递到其他机器(从库)。最常用来做读扩展、备份卸载和故障切换的基础环境。

一、复制是怎么回事

复制的基础是 binlog。一条写事务在主库上提交后,变更被记录进 binlog。从库的 IO 线程连接主库、把 binlog 内容拉到本地保存为 relay log,然后由 SQL 线程(或并行 applier 线程)从 relay log 里取事件并执行。

text
主库写入 → binlog → 从库 IO 线程拉取 → relay log → SQL 线程回放

每个对象的职责:

对象在哪做什么
binlog主库记录已提交事务的逻辑变更
IO 线程从库连主库、拉 binlog、存为 relay log
relay log从库中转日志,binlog 的本地副本
SQL/applier 线程从库读取 relay log 并逐条执行
GTID全局给每个事务一个全局编号,替代手动管理 binlog 文件和位点

两种复制方式。传统方式靠 binlog 文件名和 position 定位——mysql-bin.000123:456789,手工维护,主从切换时要记住位点。GTID 方式每个事务自带唯一的全局 ID,从库能自动判断哪些事务已经执行过,重搭、切换和故障恢复都更方便。新环境优先用 GTID。

MySQL 8.0 术语更新:

旧叫法新叫法对应的命令
mastersourceSHOW REPLICA STATUS
slavereplicaCHANGE REPLICATION SOURCE TO

实际环境里新旧两套都能见到。新搭建用新术语和 GTID,存量环境按版本支持来。

默认复制是异步的:主库提交事务后不等待从库确认。好处是主库写入速度不受从库影响;代价是主库宕机时,最后一部分事务可能还在网络上没到达从库——这些事务就丢了。半同步复制要求至少一个从库确认收到日志后主库才返回"提交成功",降低丢失概率,但增加了提交延迟。

二、主库配置

ini
[mysqld]
server_id=1                                  # 复制拓扑内唯一
log_bin=/data/mysql/logs/mysql-bin           # 开启 binlog
binlog_format=ROW                            # 行格式
binlog_expire_logs_seconds=604800            # 保留 7 天
gtid_mode=ON                                 # 启用 GTID
enforce_gtid_consistency=ON                  # 强制 GTID 一致性

server_id 在整个复制拓扑内必须唯一。两个实例用同一个 server_id,复制会出奇怪的环回问题。

binlog_format=ROW 记录的是行级别的变更("这一行从 A 变成了 B"),而不是 SQL 语句本身。ROW 格式在复制和回放时更精确——比如 UPDATE ... LIMIT 1 在 STATEMENT 格式下可能在主从上行为不一致,ROW 格式则不会。

重启后确认配置生效:

sql
SHOW VARIABLES LIKE 'log_bin';
SHOW VARIABLES LIKE 'server_id';
SHOW VARIABLES LIKE 'gtid_mode';

三、创建复制账号

只给复制需要的权限,不多给:

sql
CREATE USER 'repl'@'10.0.%' IDENTIFIED BY 'ReplPassword_123!';

GRANT REPLICATION SLAVE, REPLICATION CLIENT
ON *.* TO 'repl'@'10.0.%';

复制账号不需要 DML 和 DDL 权限——它只需要拉取 binlog 和查看复制状态。

四、从库初始化

从库需要从主库拿到一份一致性数据做起点。

小库用 mysqldump,带 GTID 信息:

bash
mysqldump -uroot -p \
  --all-databases \
  --single-transaction \
  --master-data=2 \
  --routines --triggers --events \
  > full.sql

导入从库:

bash
mysql -uroot -p < full.sql

大库用 XtraBackup 或存储快照更实际。核心原则只有一条:从库的初始数据和复制起点必须对得上——数据是哪个时间点的,复制就从哪个 GTID 或 binlog 位置开始。

五、从库配置和启动复制

ini
[mysqld]
server_id=2
relay_log=/data/mysql/logs/relay-bin
read_only=ON
super_read_only=ON
gtid_mode=ON
enforce_gtid_consistency=ON

read_only=ON 防止普通用户误写入从库;super_read_only=ON 进一步限制有 SUPER 权限的账号也写入不进来。从库只应该接收复制写入。

GTID 方式配置复制源并启动:

sql
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='10.0.0.10',
  SOURCE_PORT=3306,
  SOURCE_USER='repl',
  SOURCE_PASSWORD='ReplPassword_123!',
  SOURCE_AUTO_POSITION=1;

START REPLICA;

SOURCE_AUTO_POSITION=1 告诉 MySQL 用 GTID 自动协商位置,不需要人为指定 binlog 文件和位点。

查看复制状态:

sql
SHOW REPLICA STATUS\G

需要确认的关键字段:

字段正常值
Replica_IO_RunningYes
Replica_SQL_RunningYes
Last_IO_Error
Last_SQL_Error
Seconds_Behind_Source尽量小,单位为秒

六、复制延迟

Seconds_Behind_Source 是估算值,表示从库 SQL 线程回放到当前时间的差距。它为 0 不代表从库一定和主库完全一致——可能刚拉到的 binlog 还没来得及回放。但持续变大就是延迟的信号。

常见导致延迟的原因:

原因为什么拖慢
主库上有大事务从库单线程回放一个大事务需要很长时间
从库硬件弱CPU 或磁盘 IO 跟不上主库的写入速度
并行复制没开或不够SQL 线程默认单线程,回放效率有限
DDL 阻塞ALTER TABLE 在从库上执行会阻塞后续回放
网络带宽不够IO 线程拉取 binlog 本身速度就跟不上

开启并行复制:

ini
[mysqld]
replica_parallel_workers=4
replica_parallel_type=LOGICAL_CLOCK

并行复制能让没有冲突的事务同时回放,但大事务、热点行更新和 DDL 仍然可能成为瓶颈——它们本身就不可并行。

复制延迟要和读写分离、高可用切换一起考虑。从库落后太多时,把读请求发给它看到的是旧数据;高可用切换选到落后节点,切换后数据丢失就不仅仅是"延迟"了。

七、只读保护

确保从库不会被人误写:

sql
SET GLOBAL read_only = ON;
SET GLOBAL super_read_only = ON;

别忘记配置文件也要写进去,否则重启后只读就失效了。read_only 对有 SUPER 权限的用户无效,super_read_only 才真正把所有人都限制住。

八、复制异常排查

IO 线程出错(连接、拉取、认证相关):

sql
SHOW REPLICA STATUS\G
-- 看 Last_IO_Error

常见原因:网络不通、复制账号密码错、防火墙拦截、主库 binlog 已清理等。

SQL 线程出错(回放 binlog 内容时的错误):

sql
-- 看 Last_SQL_Error

常见原因:

错误可能原因
duplicate key从库已经存在主库想插入的行
table doesn't exist主库建了表但从库没有(DDL 没同步)
can't find record主库更新或删除的行在从库不存在

跳过某个错误事务要非常小心。传统方式 SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1 会直接跳过一个事务——如果这个事务里有很重要的数据,从库就永久丢了这段数据。GTID 环境下跳过更复杂(需要注入空事务)。跳过前必须确认:这个事务是什么内容、影响了哪些表、跳过会不会导致主从数据不一致。

九、延迟从库——误删恢复的安全网

故意让从库落后一段时间再回放。如果主库上发生了误操作,在被延迟的这段时间内,从库还没执行到那条误操作——可以从中恢复数据:

sql
CHANGE REPLICATION SOURCE TO SOURCE_DELAY = 3600;

这个从库故意落后 1 小时。它不是用来扛读流量的——它落后——它是专门用来做误操作恢复的。延迟从库和高可用从库不是同一类东西:前者不能承接业务读请求,后者要保持尽量小的延迟。

十、日常巡检

每天或每班巡检要看几样东西:

检查项怎么看什么算正常
复制线程运行状态SHOW REPLICA STATUS\GIO 和 SQL 都是 Yes
延迟同上,看 Seconds_Behind_Source在业务可接受范围内
只读保护SHOW VARIABLES LIKE 'super_read_only'ON
binlog 不会过期太快SHOW BINARY LOGS覆盖全量备份周期
错误日志tail error.log无异常
主从数据一致性pt-table-checksum 等工具无差异

主从复制能提高读能力和恢复弹性,但从库不是备份。从库会忠实地回放主库上的每一笔变更——包括 DROP TABLEDELETE FROM ... WHERE 1=1。误删误改发生后,从库上的数据也会同步被删改。备份才有回退到指定时间点的能力。