Appearance
主从复制详解
一、什么是主从复制?
主从复制(Master-Slave Replication)是MySQL提供的数据同步机制,用于将主库(Master)的交易数据异步/半同步复制到从库(Slave)。
核心作用:
- 读写分离:主库处理写操作(INSERT/UPDATE/DELETE),从库处理读操作(SELECT),缓解主库压力;
- 数据备份:从库作为主库的冗余副本,避免因主库故障导致数据丢失;
- 高可用:主库宕机时,从库可快速切换为主库,保证服务连续性;
- 横向扩展:通过增加从库数量,提升读性能(比如应对高并发查询)。
二、主从复制的工作原理
主从复制的核心依赖3个组件和3步流程,我们用“binlog日志传递+relay log重放”来概括:
1. 关键组件
- 二进制日志(Binlog):主库生成的交易日志,记录所有修改数据的操作(如DML、DDL),是复制的“源数据”;
- IO线程(Slave IO Thread):从库启动的线程,负责连接主库,读取主库的binlog并写入从库的中继日志(Relay Log);
- SQL线程(Slave SQL Thread):从库启动的线程,负责读取中继日志,执行其中的SQL语句,将主库的数据变更同步到从库。
2. 复制流程(异步模式,默认)
主库(Master) 从库(Slave)
| |
| 1. 执行SQL(写操作) |
| 2. 写入Binlog |
| |
|--------------------------->| 3. IO线程读取Binlog,写入Relay Log
| |
| | 4. SQL线程读取Relay Log,执行SQL
| | 5. 数据同步完成3. 关键细节
- Binlog的作用:主库的所有写操作都会先写入binlog(顺序写,性能高),再修改内存中的数据(InnoDB的事务提交逻辑:redo log -> binlog -> 提交);
- Relay Log的作用:从库的“缓冲日志”,避免直接读取主库binlog导致主库压力过大,同时支持断点续传(比如网络中断后,从上次断开的位置继续同步);
- 异步 vs 半同步 vs 全同步:
- 异步(默认):主库写完binlog就返回客户端,不等待从库确认,性能最高但可能丢数据(比如主库宕机时,未同步的binlog丢失);
- 半同步(Semi-Sync):主库需要等待至少一个从库收到binlog并返回确认(ack),再返回客户端,牺牲少量性能(延迟约1ms),但保证数据不丢;
- 全同步(Full-Sync):主库需要等待所有从库都应用了binlog,再返回客户端,安全性最高但性能最差(几乎不用)。
三、主从复制的配置步骤
以一主一从为例,讲解基础配置(假设主库IP:192.168.1.100,从库IP:192.168.1.200)。
1. 主库(Master)配置
步骤1:修改my.cnf(或my.ini)
开启binlog,设置主库唯一标识(server-id):
ini
[mysqld]
# 开启binlog,指定日志文件名前缀(如mysql-bin.000001)
log_bin = mysql-bin
# 主库唯一ID(必须≠从库)
server_id = 100
# binlog格式(推荐ROW,后面讲原因)
binlog_format = ROW
# 可选:只复制指定数据库(如db_test)
# binlog_do_db = db_test
# 可选:忽略指定数据库
# binlog_ignore_db = mysql步骤2:重启主库
bash
systemctl restart mysqld # CentOS
service mysql restart # Ubuntu步骤3:创建复制用户
主库需要创建一个仅用于复制的用户(授予REPLICATION SLAVE权限):
sql
CREATE USER 'repl'@'192.168.1.%' IDENTIFIED BY 'repl_password'; -- 允许从库网段连接
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%';
FLUSH PRIVILEGES; -- 刷新权限步骤4:获取主库状态
锁表(避免同步过程中数据变更),记录binlog文件名和位置(后续从库需要用):
sql
FLUSH TABLES WITH READ LOCK; -- 锁表(只读)
SHOW MASTER STATUS; -- 查看主库状态输出示例:
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000003 | 1234 | db_test | mysql |
+------------------+----------+--------------+------------------+注意:记住File(mysql-bin.000003)和Position(1234),后续从库需要用到。
解锁表(完成后执行):
sql
UNLOCK TABLES;2. 从库(Slave)配置
步骤1:修改my.cnf
设置从库唯一标识(server-id),关闭binlog(可选,若从库不需要作为其他从库的主库):
ini
[mysqld]
# 从库唯一ID(必须≠主库)
server_id = 200
# 可选:关闭binlog(若从库不做为主库)
# skip_log_bin
# 可选:中继日志文件名前缀(默认relay-bin)
# relay_log = relay-bin步骤2:重启从库
bash
systemctl restart mysqld步骤3:初始化从库数据(可选但重要)
若主库已有数据,从库需要先同步主库的初始数据(否则复制会从当前binlog位置开始,导致数据不一致)。
常用方法:
- mysqldump:适用于小数据量(如10G以内):bash
# 主库导出数据(包含binlog位置) mysqldump -u root -p --all-databases --master-data=2 > master_data.sql # 从库导入数据 mysql -u root -p < master_data.sql - XtraBackup:适用于大数据量(如100G以上),支持热备份(不锁表),推荐生产环境使用。
步骤4:配置从库连接主库
执行CHANGE MASTER TO命令,指定主库信息、复制用户、binlog位置:
sql
CHANGE MASTER TO
MASTER_HOST = '192.168.1.100', -- 主库IP
MASTER_USER = 'repl', -- 复制用户
MASTER_PASSWORD = 'repl_password', -- 复制用户密码
MASTER_LOG_FILE = 'mysql-bin.000003', -- 主库的binlog文件名(来自SHOW MASTER STATUS)
MASTER_LOG_POS = 1234; -- 主库的binlog位置(来自SHOW MASTER STATUS)步骤5:启动复制
sql
START SLAVE; -- 启动从库复制线程
SHOW SLAVE STATUS\G; -- 查看从库状态关键状态检查(SHOW SLAVE STATUS输出):
Slave_IO_Running:Yes(IO线程正常运行);Slave_SQL_Running:Yes(SQL线程正常运行);Seconds_Behind_Master:0(从库延迟主库0秒,理想状态)。
四、Binlog格式(重要)
Binlog有3种格式,直接影响复制的正确性和性能:
1. STATEMENT(基于语句)
- 特点:记录执行的SQL语句(如
UPDATE user SET name='xxx' WHERE id=1); - 优点:日志体积小,性能高;
- 缺点:可能导致主从数据不一致(如使用
NOW()、RAND()等函数,或存储过程中的非确定性操作); - 适用场景:无复杂函数或存储过程的简单业务。
2. ROW(基于行)
- 特点:记录每行数据的变更细节(如
id=1的行,name从old变为new); - 优点:主从数据绝对一致(避免非确定性操作的问题);
- 缺点:日志体积大(如批量更新10万行,会记录10万行的变更),性能略低;
- 适用场景:生产环境推荐(尤其是有复杂业务逻辑的系统)。
3. MIXED(混合模式)
- 特点:自动选择STATEMENT或ROW格式(简单语句用STATEMENT,复杂语句用ROW);
- 优点:平衡性能和正确性;
- 缺点:不如ROW格式稳定(某些场景仍可能出现不一致);
- 适用场景:过渡阶段或对性能要求极高的场景。
结论:生产环境优先选择ROW格式,确保数据一致性。
五、常见复制架构
根据业务需求,主从复制可以扩展为以下架构:
1. 一主一从
- 结构:1个主库 + 1个从库;
- 适用场景:小规模应用,用于读写分离或备份。
2. 一主多从
- 结构:1个主库 + N个从库;
- 适用场景:高并发读场景(如电商首页查询),通过增加从库数量提升读性能;
- 注意:主库的binlog需要发送给所有从库,会增加主库的网络压力(可通过级联复制缓解)。
3. 级联复制(主->从->从)
- 结构:主库(Master)→ 中间从库(Intermediate Slave)→ 下游从库(Slave);
- 适用场景:主库网络压力大(如10个从库),中间从库作为“转发者”,减轻主库的binlog发送压力;
- 配置:中间从库需要开启
log_slave_updates(允许将从主库同步的binlog写入自己的binlog,供下游从库读取)。
4. 双主复制(互为主从)
- 结构:两个库互为主库和从库(A是B的主库,B是A的主库);
- 适用场景:高可用(如主库宕机后,从库自动切换为主库),或需要双向同步的场景(如多地域部署);
- 注意:需要避免循环复制(通过
server-id过滤,或使用replicate-ignore-db)。
六、常见问题与优化
1. 复制延迟(Seconds_Behind_Master 增大)
- 原因:
- 主库写压力过大(如批量插入100万行);
- 从库读压力过大(如从库同时处理大量查询);
- 网络延迟(主库与从库之间网络慢);
- SQL线程执行慢(如从库的事务需要锁表,或未开启并行复制)。
- 解决方法:
- 优化主库:拆分大事务(如将100万行插入拆分为10次,每次10万行);
- 优化从库:增加从库数量(分散读压力),或升级从库硬件(如更大的内存、更快的磁盘);
- 开启并行复制:MySQL 5.7+支持基于组提交的并行复制(
slave_parallel_type = LOGICAL_CLOCK,slave_parallel_workers = 4,设置并行线程数); - 使用半同步复制:减少异步复制的延迟(但会增加主库的响应时间)。
2. 复制中断(Slave_IO_Running 或 Slave_SQL_Running 为 No)
- 常见原因:
- 从库的relay log损坏(如磁盘故障);
- 主库的binlog被删除(如主库设置了
expire_logs_days,自动删除旧binlog,而从库未同步完); - 从库执行SQL时出错(如主库有某张表,从库没有,导致
Table not found)。
- 解决方法:
- 重新初始化从库:若从库数据丢失或损坏,用mysqldump或XtraBackup重新同步主库数据;
- 跳过错误事务(不推荐,可能导致数据不一致):sql
STOP SLAVE; SET GLOBAL sql_slave_skip_counter = 1; -- 跳过1个事务 START SLAVE; - 使用GTID(全局事务标识符):避免手动指定binlog位置,自动跳过已执行的事务(后面讲GTID)。
3. 数据不一致(主从数据不同步)
- 原因:
- Binlog格式使用STATEMENT(非确定性操作);
- 从库执行了手动修改(如直接在从库插入数据);
- 复制中断后未正确恢复(如跳过了关键事务)。
- 解决方法:
- 使用ROW格式:确保主从数据一致;
- 禁止从库写操作:从库只用于读,避免手动修改;
- 定期校验数据:使用
pt-table-checksum工具(Percona Toolkit)校验主从数据一致性,若不一致用pt-table-sync修复。
七、GTID(全局事务标识符)—— 现代复制的核心
传统复制需要手动指定主库的binlog文件名和位置(MASTER_LOG_FILE、MASTER_LOG_POS),故障切换时非常麻烦(如主库宕机,需要找到从库的最后一个binlog位置,再切换到新主库)。
GTID(Global Transaction ID) 是MySQL 5.6+引入的全局唯一事务标识符,格式为:server-id:transaction-id(如100:123,表示主库100的第123个事务)。
核心优势:
- 自动同步:从库会记录已执行的GTID,主库会自动发送未执行的GTID,无需手动指定binlog位置;
- 故障切换简单:主库宕机后,从库切换为主库,其他从库只需连接新主库,自动同步未执行的事务;
- 避免循环复制:双主复制中,通过GTID过滤已执行的事务,避免循环执行。
GTID配置步骤(主库)
修改my.cnf:
ini
[mysqld]
server_id = 100
log_bin = mysql-bin
binlog_format = ROW
# 开启GTID
gtid_mode = ON
enforce_gtid_consistency = ON -- 强制GTID一致性(避免非GTID事务)GTID配置步骤(从库)
修改my.cnf:
ini
[mysqld]
server_id = 200
gtid_mode = ON
enforce_gtid_consistency = ON
# 允许从库将GTID写入自己的binlog(级联复制需要)
log_slave_updates = ONGTID复制启动命令
从库连接主库时,无需指定MASTER_LOG_FILE和MASTER_LOG_POS,只需指定MASTER_AUTO_POSITION = 1(自动获取GTID位置):
sql
CHANGE MASTER TO
MASTER_HOST = '192.168.1.100',
MASTER_USER = 'repl',
MASTER_PASSWORD = 'repl_password',
MASTER_AUTO_POSITION = 1; -- 自动同步GTID
START SLAVE;