MySQL 主从复制:原理与实战
一、理论
什么是主从复制
主从复制是一种数据库复制技术:把主库(Master / Source)的数据变更,实时或准实时地复制到一个或多个从库(Slave / Replica)。
本质是「主库记录变更 → 从库拉取 → 从库重放 」,因此它是异步复制:默认不保证零丢失,只保证最终一致。
原理与流程
text
主库:数据变更 → 写入 binlog(二进制日志)
↓
从库 I/O 线程:拉取主库 binlog → 写入本地 relay log(中继日志) # 所以才会占 CPU 和 IO
↓
从库 SQL 线程:读取 relay log → 重放变更 → 与主库保持一致
复制模式
| 模式 | 说明 | 特点 |
|---|---|---|
| SBR(基于语句) | 记录执行的 SQL 语句 | 日志小,但某些函数(如 NOW()、UUID())可能导致不一致 |
| RBR(基于行) | 记录每一行的变更 | 最安全、最常用,日志量较大 |
| Mixed(混合) | 由 MySQL 自动在两者间选择 | 兼容性折中 |
生产环境推荐
binlog_format = ROW。
主从复制的用途与意义
| 场景 | 说明 |
|---|---|
| 读写分离 | 主库负责写,从库负责读,提升整体吞吐 |
| 数据热备 | 主库故障时可快速切换到从库 |
| 负载均衡 | 多从库分散高并发查询压力 |
| 数据分析 | 在从库执行统计查询,避免影响主库 |
注意:因为是异步复制,从库从主库拉取会有延迟;要想立马写入立马读,可以在主库上进行。
二、实战
环境准备
- 主从 MySQL 版本一致(大版本必须相同,建议小版本也一致)
server-id全局唯一- 主从网络互通,开放 3306 端口(防火墙)
- 两台机器时间同步(chrony / NTP),字符集与排序规则一致
主库配置
1. 配置文件
bash
vim /etc/my.cnf
ini
[mysqld]
server-id = 1 # 主库唯一标识
log-bin = mysql-bin # 启用二进制日志
binlog_format = ROW # 推荐使用行格式
binlog_expire_logs_seconds = 604800 # binlog 保留 7 天
max_binlog_size = 512M
修改完以后记得重启 MySQL 服务:
bash
systemctl restart mysqld
提示 :自定义日志路径(如
/var/log/mysql/mysql-bin.log)时,目录必须存在且属主为 mysql,否则服务启动失败;CentOS 上还会有 SELinux 标签问题。练习环境直接用默认路径最省事。
2. 配置从库登录账号
配置文件写完以后,还得配置从库登录账号,不要让从库直接有 root 权限:
sql
CREATE USER 'repl'@'从库IP' IDENTIFIED BY 'Repl@2026';
-- 复制从库权限,允许该用户连接主库并读取二进制日志(binlog)
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'172.22.4.4';
FLUSH PRIVILEGES;
-- 查看账号
SELECT user, host, plugin FROM mysql.user WHERE user = 'repl';
3. 查看主库状态
从库部署好后,我们可以输入命令查看主库状态:
sql
-- MySQL 8.4 之前
SHOW MASTER STATUS;
-- MySQL 8.4 及之后
SHOW BINARY LOG STATUS;
输出样例如下:
text
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 | 747 | | |
+------------------+----------+--------------+------------------+
记录 File 和 Position,从库配置会用到。
4. 主库已有数据怎么同步
但是还有个问题:主从复制完成之前,主库的数据该怎么同步?
在从库配置前,如果主库有数据,可以先进行一个全量备份导入到从库,保证初期数据一致性:
bash
mysqldump -uroot -p --single-transaction --master-data=2 \
--routines --triggers --events --all-databases \
> /backup/full_$(date +%F).sql
再把这个文件导入从库。
从库配置
ini
[mysqld]
server-id = 2 # 必须与主库不同
relay-log = relay-bin # 中继日志
read_only = 1 # 普通用户只读,防止误写
super_read_only = 1 # 超级用户也只读
replica_parallel_workers = 4 # 多线程复制,缓解延迟
replica_parallel_type = LOGICAL_CLOCK
replica_preserve_commit_order = 1
提示 :
read_only和super_read_only在主从切换时需要去掉。
配置主从连接
主从库配置都好了以后,就可以配置连接:
IP + 端口定位主库,数据库账户密码获取权限,再加上之前主库查的
File+Position。
sql
CHANGE REPLICATION SOURCE TO
SOURCE_HOST = '主库IP',
SOURCE_USER = 'repl',
SOURCE_PASSWORD = 'Repl@2026',
SOURCE_PORT = 3306,
SOURCE_LOG_FILE = 'mysql-bin.000001',
SOURCE_LOG_POS = 747,
GET_SOURCE_PUBLIC_KEY = 1, -- 8.0 默认 caching_sha2_password 时需要
SOURCE_SSL = 0;
START REPLICA;
注意:这段是要在数据库里执行。
另一种模式:GTID 模式
GTID 模式(推荐,免去手工算位置)
主从都配置:
ini
gtid_mode = ON
enforce_gtid_consistency = ON
从库只需:
sql
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='172.22.4.3',
SOURCE_USER='repl',
SOURCE_PASSWORD='Repl@2025',
SOURCE_AUTO_POSITION = 1;
START REPLICA;
验证主从复制的效果
到这里主从复制就完成了,我们来验证一下主从复制的效果。
查看同步状态:
sql
SHOW REPLICA STATUS\G; -- MySQL 8.0.22 及之后
SHOW SLAVE STATUS\G; -- MySQL 8.0.22 之前
查询到的结果对照下面的指标表即可。
重点指标:
| 指标 | 期望值 | 说明 |
|---|---|---|
Replica_IO_Running |
Yes | I/O 线程正常拉取主库 binlog |
Replica_SQL_Running |
Yes | SQL 线程正常重放 |
Seconds_Behind_Source |
接近 0 | 复制延迟秒数 |
Last_IO_Error |
空 | I/O 线程错误 |
Last_SQL_Error |
空 | SQL 线程错误 |
旧版本命令显示的字段名是
Slave_IO_Running/Slave_SQL_Running。
之后可以在主库创建数据库表,从库查看来验证,这里就不展开了。
三、主从常用命令
sql
-- 查看状态
SHOW REPLICA STATUS\G;
-- 停止/启动复制
STOP REPLICA;
START REPLICA;
-- 重置主从关系(清空复制信息)
STOP REPLICA;
RESET REPLICA ALL;
-- 旧语法(8.0 之前)
STOP SLAVE;
RESET SLAVE ALL;
主从切换(从库提升为主库)的大致操作:
sql
STOP REPLICA;
RESET REPLICA ALL;
SET GLOBAL read_only = OFF;
SET GLOBAL super_read_only = OFF;
注意:这些命令都在从库数据库执行。
四、常见故障与处理
| 问题 | 原因 | 解决 |
|---|---|---|
| 连接失败(2003) | 网络不通、防火墙未放行、账号权限不足 | 检查 3306、云安全组、账号是否限定 IP |
| 认证失败 | 密码错误或认证插件不兼容 | GET_SOURCE_PUBLIC_KEY=1,或改用 mysql_native_password |
Last_IO_Error: 1236 |
指定的 binlog 文件不存在(已被清理) | 重新全量导出并重新配置位置 |
| 主键冲突(1062) | 从库被写入过数据或数据不一致 | 检查 read_only,校验数据后重建从库 |
| 同步延迟过高 | 大事务、慢查询、单线程重放、从库资源不足 | 开启多线程复制、拆分大事务、优化慢查询、提升硬件 |
| 主从数据不一致 | 误操作、跳过错误、非确定性语句 | 用 pt-table-checksum 校验并修复 |
| SQL 线程停止 | 表不存在、字段不匹配等 | 先看 Last_SQL_Error,修复后重启复制 |