同步原理
Mysql 主从复制的流程

- 主库db的更新事件(update、insert、delete)被写到binlog
- 主库创建一个binlog dump thread,把binlog的内容发送到从库
- 从库启动并发起连接,连接到主库
- 从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log
- 从库启动之后,创建一个SQL线程,从relay log里面读取内容,从Exec_Master_Log_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db
MySQL 主从形式
- 一主一从
- 一主多从,提高系统的读性能
- 多主一从 (从5.7开始支持)
- 双主复制
- 级联复制
MySQL 主从复制原理详解
MySQL主从复制涉及到三个线程,一个运行在主节点(log dump thread),其余两个(I/O thread, SQL thread)运行在从节点,如下图所示:

对于每一个主从连接,都需要三个进程来完成。当主节点有多个从节点时,主节点会为每一个当前连接的从节点建一个binary log dump 进程,而每个从节点都有自己的I/O进程,SQL进程。从节点用两个线程将从主库拉取更新和执行分成独立的任务,这样在执行同步数据任务的时候,不会降低读操作的性能。比如,如果从节点没有运行,此时I/O进程可以很快从主节点获取更新,尽管SQL进程还没有执行。如果在SQL进程执行之前从节点服务停止,至少I/O进程已经从主节点拉取到了最新的变更并且保存在本地relay日志中,当服务再次起来之后,就可以完成数据的同步。
要实施复制,首先必须打开Master 端的binary log(bin-log)功能,否则无法实现。
同步配置
主服务器配置
Bash
cat > /etc/my.cnf/ << EOF
[mysqld]
# ===================== 基础全局配置 =====================
# 服务ID(主库必须唯一,建议设为整数,如1;从库需不同,如2、3...)
server-id = 1
# 数据库默认字符集(统一字符集避免乱码)
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# 默认存储引擎(InnoDB适配事务、行级锁,适合绝大多数场景)
default-storage-engine = InnoDB
# 禁用符号链接(安全加固,避免文件篡改)
skip-symbolic-links = 1
# 监听地址(0.0.0.0允许所有IP访问,生产建议指定内网IP)
bind-address = 0.0.0.0
# 端口(默认3306,可根据安全需求修改)
port = 3306
# ===================== 主从复制核心配置 =====================
# 开启二进制日志(主库必须开启,用于同步)
log_bin = /var/lib/mysql/mysql-bin
# binlog日志格式(推荐ROW,行级复制,精度高,避免语句级复制的兼容问题)
binlog_format = ROW
# binlog日志过期清理时间(7天,避免日志占满磁盘)
expire_logs_days = 7
# binlog单个文件大小上限(500M,按需调整)
max_binlog_size = 500M
# 跳过主从复制的系统库(避免同步mysql/information_schema等系统表)
binlog-ignore-db = mysql
binlog-ignore-db = information_schema
binlog-ignore-db = performance_schema
binlog-ignore-db = sys
# 仅同步指定业务库(可选,按需配置,多个库需重复写)
# binlog-do-db = your_business_db1
# binlog-do-db = your_business_db2
# 避免自增主键冲突(主库自增步长1,偏移1;从库建议设为步长1,偏移2)
auto_increment_increment = 1
auto_increment_offset = 1
# 记录binlog时不忽略GTID(MySQL 5.7+ GTID模式必备)
enforce_gtid_consistency = ON
# 开启GTID(推荐开启,简化主从切换、故障恢复)
gtid_mode = ON
# ===================== InnoDB性能优化 =====================
# 缓冲池大小(建议设为物理内存的50%-70%,如16G内存设为10G)
innodb_buffer_pool_size = 1G
# 缓冲池实例数(多核CPU建议拆分,如8核设为4)
innodb_buffer_pool_instances = 1
# 日志文件大小(建议256M-1G,不要超过4G)
innodb_log_file_size = 256M
# 日志缓冲区大小
innodb_log_buffer_size = 64M
# 事务提交方式(1=每次提交刷盘,最安全;0/2性能高但有丢失风险)
innodb_flush_log_at_trx_commit = 1
# 开启独立表空间(便于单表备份/恢复)
innodb_file_per_table = ON
# 刷新方式(O_DIRECT减少系统缓存,提升IO效率)
innodb_flush_method = O_DIRECT
# ===================== 日志与安全 =====================
# 错误日志路径(便于排查故障)
log_error = /var/lib/mysql/mysql-error.log
# 慢查询日志(开启后记录执行时间超过long_query_time的SQL)
slow_query_log = ON
slow_query_log_file = /var/lib/mysql/mysql-slow.log
long_query_time = 2
# 记录所有未使用索引的查询(便于优化)
log_queries_not_using_indexes = ON
# 关闭查询缓存(MySQL 8.0已移除,5.7建议关闭,避免性能损耗)
query_cache_type = 0
query_cache_size = 0
# ===================== 其他优化 =====================
# 最大连接数(按需调整,避免连接数不足)
max_connections = 1000
# 连接超时时间(秒)
wait_timeout = 600
interactive_timeout = 600
# 临时表大小上限
tmp_table_size = 64M
max_heap_table_size = 64M
[mysqld_safe]
# 错误日志追加模式(避免覆盖)
log-error=/var/lib/mysql/mysql-safe.log
# 进程文件路径
pid-file=/var/run/mysqld/mysqld.pid
[mysql]
# 客户端默认字符集
default-character-set = utf8mb4
EOF
只重启slave某一线程
SQL
-- 停止 IO 线程
STOP REPLICA IO_THREAD;
-- 启动 IO 线程
START REPLICA IO_THREAD;
-- 停止 IO 线程
STOP REPLICA IO_THREAD;
-- 启动 IO 线程
START REPLICA IO_THREAD;
-- MySQL 8.0+:语法兼容 STOP/START SLAVE SQL_THREAD,也可使用新语法(效果一致):
STOP REPLICA SQL_THREAD; -- 替代 STOP SLAVE SQL_THREAD
START REPLICA SQL_THREAD; -- 替代 START SLAVE SQL_THREAD
常见异常处理
SQL 线程启动失败(Slave_SQL_Running: No)
大概率是主从数据不一致或 SQL 执行错误,需先定位错误:
SQL
-- 查看错误信息
SHOW SLAVE STATUS\G; -- 重点看 Last_SQL_Error 字段
常见解决方式:
- 跳过错误(临时应急,谨慎使用):
SQL
-- 跳过 1 个错误(适用于主键冲突、表不存在等非核心错误)
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE SQL_THREAD;
- 修复数据不一致:通过
pt-table-checksum/pt-table-sync校验并同步数据,再重启 SQL 线程。
数据修复参考文档:https://github.com/percona/percona-toolkit?tab=readme-ov-file
如何解析查看binlog内容
Bash
# 按照时间查看
mysqlbinlog --start-datetime="2020-03-11 20:05:00" \
--stop-datetime="2020-03-11 20:08:00" --verbose \
binlog.000447
# 按照事件查看
mysqlbinlog --no-defaults --verbose --base64-output=DECODE-ROWS binlog.000447 --start-position 948973804