Appearance
主从复制
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 术语更新:
| 旧叫法 | 新叫法 | 对应的命令 |
|---|---|---|
| master | source | SHOW REPLICA STATUS |
| slave | replica | CHANGE 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=ONread_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_Running | Yes |
Replica_SQL_Running | Yes |
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\G | IO 和 SQL 都是 Yes |
| 延迟 | 同上,看 Seconds_Behind_Source | 在业务可接受范围内 |
| 只读保护 | SHOW VARIABLES LIKE 'super_read_only' | ON |
| binlog 不会过期太快 | SHOW BINARY LOGS | 覆盖全量备份周期 |
| 错误日志 | tail error.log | 无异常 |
| 主从数据一致性 | pt-table-checksum 等工具 | 无差异 |
主从复制能提高读能力和恢复弹性,但从库不是备份。从库会忠实地回放主库上的每一笔变更——包括 DROP TABLE 和 DELETE FROM ... WHERE 1=1。误删误改发生后,从库上的数据也会同步被删改。备份才有回退到指定时间点的能力。