MySQL 主从复制指南
MySQL 主从复制是高可用数据库架构的基础。核心流程:主库 Binlog → IO 线程拉取 → Relay Log → SQL 线程执行。
复制类型对比
| 类型 | 延迟 | 数据安全 | 适用场景 |
|---|---|---|---|
| 异步复制(默认) | 无等待 | 可能丢数据 | 性能优先,容忍少量丢失 |
| 半同步复制 | 等待至少 1 个从库确认 | 高 | 生产推荐,平衡性能与安全 |
| 全同步复制(Group Replication) | 等待所有从库 | 零丢失 | 强一致性要求 |
Binlog 格式选择
| 格式 | 原理 | 优点 | 缺点 | 生产推荐 |
|---|---|---|---|---|
| STATEMENT | 记录 SQL 语句 | 日志量小 | NOW()/UUID() 可能不一致 | ❌ |
| ROW | 记录被修改行完整内容 | 一致性强 | 日志量大 | ✅ |
| MIXED | 默认 STATEMENT,需要时切 ROW | 折中 | 不可预测 | ❌ |
GTID 复制
GTID = source_id:transaction_id,自动化复制位点管理。
[mysqld]
gtid_mode = ON
enforce_gtid_consistency = ON
log_slave_updates = ON
-- 关键命令
SHOW MASTER STATUS;
SHOW SLAVE STATUS\G
SELECT @@GLOBAL.gtid_executed;
-- 手动跳过失败事务(GTID 模式)
STOP SLAVE;
SET GTID_NEXT = 'source_id:transaction_id';
BEGIN; COMMIT;
SET GTID_NEXT = 'AUTOMATIC';
START SLAVE;
常见架构与搭建
一主一从标准搭建
# 主库备份
mysqldump --single-transaction --source-data=2 --all-databases > full_backup.sql
# 从库恢复
mysql -u root -p < full_backup.sql
-- 从库配置 GTID 复制
CHANGE MASTER TO
MASTER_HOST = 'master.example.com',
MASTER_USER = 'repl',
MASTER_PASSWORD = 'repl_password',
MASTER_AUTO_POSITION = 1;
START SLAVE;
-- 验证
SHOW SLAVE STATUS\G
-- Slave_IO_Running: Yes
-- Slave_SQL_Running: Yes
-- Seconds_Behind_Master: 0
架构选择
| 架构 | 特点 | 适用场景 |
|---|---|---|
| 一主一从 | 基础容灾 | 中小规模 |
| 一主多从 | 读写分离 | 读密集型 |
| 级联复制 | 减轻主库压力 | 跨机房、大集群 |
| 双主 | 双向写入 / 快速切换 | HA,注意主键冲突 |
半同步复制配置
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 10000; -- 10秒超时降级异步
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;
故障排查
IO 线程 Connecting
信号: Slave_IO_Running: Connecting + Last_IO_Error: error connecting to master
排查: ping / telnet / 权限检查 / max_connections
SQL 线程错误
信号: Last_SQL_Error: Could not execute ... Can't find record
修复:
-- 方案 A:跳过(临时)
SET GTID_NEXT = 'source_id:transaction_id';
BEGIN; COMMIT;
-- 方案 B:修复数据一致
pt-table-sync h=master h=slave --databases=test --execute
-- 方案 C:重新初始化从库
复制延迟过大
排查: SHOW PROCESSLIST / Innodb_log_waits / 网络延迟
修复:
STOP SLAVE;
SET GLOBAL slave_parallel_workers = 16; -- CPU 核数 50%~80%
SET GLOBAL slave_parallel_type = 'LOGICAL_CLOCK';
SET GLOBAL slave_preserve_commit_order = ON;
START SLAVE;
GTID 被清除(fatal error 1236)
根因: 主库 purged 了从库需要的 Binlog
修复: 从主库用 mysqldump --source-data=2 重新备份恢复。
监控与检查脚本
#!/bin/bash
# check_mysql_replication.sh
STATUS=$(mysql -e "SHOW SLAVE STATUS\G" 2>/dev/null)
IO=$(echo "$STATUS" | grep "Slave_IO_Running:" | awk '{print $2}')
SQL=$(echo "$STATUS" | grep "Slave_SQL_Running:" | awk '{print $2}')
LAG=$(echo "$STATUS" | grep "Seconds_Behind_Master:" | awk '{print $2}')
echo "IO: $IO | SQL: $SQL | 延迟: ${LAG}s"
[ "$IO" != "Yes" ] && echo "[严重] IO 线程异常"
[ "$SQL" != "Yes" ] && echo "[严重] SQL 线程异常"
[ "$LAG" -gt 300 ] 2>/dev/null && echo "[严重] 延迟 > 5分钟"
数据一致性校验
# 自动校验(pt-table-checksum)
pt-table-checksum --replicate=test.checksums --databases=test
mysql -e "SELECT db, tbl, DIFFS FROM test.checksums WHERE DIFFS > 0;"
# 手动校验
mysql -h master -N -e "SELECT COUNT(*), SUM(id) FROM test.orders" > /tmp/master.txt
mysql -h slave -N -e "SELECT COUNT(*), SUM(id) FROM test.orders" > /tmp/slave.txt
diff /tmp/master.txt /tmp/slave.txt && echo "一致" || echo "不一致"
排障速查表
| 问题 | 信号 | 排查方法 | 解决方案 |
|---|---|---|---|
| IO 线程 Connecting | Last_IO_Error |
ping/telnet/GRANTS | 修网络/权限/连接数 |
| SQL 线程错误 | Last_SQL_Error |
pt-table-checksum | pt-table-sync 修复 |
| 复制延迟大 | Seconds_Behind_Master 高 |
SHOW PROCESSLIST | 增加并行复制线程 |
| GTID 被清除 | Error 1236 | SHOW MASTER STATUS |
重新备份恢复 |
| Relay Log 损坏 | 错误日志 | 查看错误 | 重启复制或重建从库 |
运维清单
- 每日:
SHOW SLAVE STATUS/SHOW PROCESSLIST - 每周: 工作者线程状态 /
Innodb_log_waits - 每月: pt-table-checksum 一致性校验 / 故障切换演练 / 清理旧 Binlog
关联页面
| 页面 | 关联点 |
|---|---|
| mysql-performance-config | MySQL 性能调优与死锁排查 |
| fullstack-performance-troubleshooting | 全栈性能排障(MySQL 在其中) |
| redis-ha-replication-sentinel | Redis 主从/哨兵(对比学习数据库 HA) |