一、主从复制的基础原理与线程模型
主从复制是MySQL实现数据冗余、读写分离和故障切换的基石。它的核心逻辑并不复杂:主库把数据变更写入二进制日志(binlog),从库通过网络拉取这些日志并重新执行,最终达到两边数据一致。面试时如果只回答到这一层还不够,需要明确三个线程的分工。

主库上有一个专门的Binlog Dump线程。每当有从库连接上来,主库就会为这个从库启动一个Dump线程,负责读取binlog中的事件并发送给从库。从库这边有两个线程:I/O线程接收主库发来的binlog事件,写入本地的中继日志(relay log);SQL线程则读取relay log中的事件,在从库上执行重放。也就是说,从库的I/O线程和SQL线程是解耦的,I/O线程写得快不代表SQL线程执行得快,这正是主从延迟产生的结构性原因。
配置主从复制时,需要在主库上创建复制账号,并开启binlog。从库通过CHANGE MASTER TO命令指定主库信息,然后启动复制。下面是一段最基本的搭建命令,假设主库IP为192.168.10.101,复制账号为repl。
-- 主库执行 CREATE USER 'repl'@'%' IDENTIFIED BY 'Repl@123'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES; SHOW MASTER STATUS;
SHOW MASTER STATUS会显示当前binlog文件名和偏移量,从库配置时需要用到这两个值。从库执行CHANGE MASTER TO时,MASTER_LOG_FILE和MASTER_LOG_POS要对应主库当前的输出。配置完成后,可以执行START SLAVE启动复制。在实际面试中,面试官常会追问:如果主库已经运行一段时间,业务不能停,如何做从库初始化?这需要先通过mysqldump或xtrabackup做全量备份,并记录一致性位置,再导入从库后从该位置开始同步。
二、binlog三种格式对比与选择
binlog是主从复制的数据载体,它有三种格式:STATEMENT、ROW和MIXED。面试中常问“为什么推荐使用ROW格式”,回答时要能说清底层差异。STATEMENT格式记录的是执行的SQL语句本身,优点是日志量小,可读性高;缺点是如果语句中包含NOW()、UUID()等非确定性函数,或者涉及触发器、存储过程,主从执行结果可能不一致。
ROW格式记录每一行数据被修改前后的具体值,不记录SQL语句。它的优点是完全精确,主从不会因为函数、上下文不同而出现数据偏差,适合要求强一致的场景;缺点是日志量会明显增大,尤其是批量UPDATE或DELETE操作,可能产生大量binlog。MIXED格式则是折中方案:默认使用STATEMENT,遇到可能导致不一致的操作时自动切换为ROW。MySQL 5.7以后默认使用ROW格式,正是因为可靠性优先。
下面用一个小实验说明ROW格式下binlog的内容。假设主库执行一条UPDATE语句修改某行数据,用mysqlbinlog解析后可以看到类似下面的片段:
# at 1984 #210101 12:00:00 server id 1 end_log_pos 2105 CRC32 0x4f3b2a1c # Query thread_id=10 exec_time=0 error_code=0 SET TIMESTAMP=1609473600; UPDATE `test`.`t_user` SET `age`=21 WHERE `id`=100; # at 2105 #210101 12:00:00 server id 1 end_log_pos 2150 CRC32 0x7c9e1f2d ### UPDATE `test`.`t_user` ### WHERE ### @1=100 ### @2='张三' ### @3=20 ### SET ### @1=100 ### @2='张三' ### @3=21
从这段输出可以看出,ROW格式记录了完整的行数据变更前后镜像。在面试中如果能提到这一点,并补充说明STATEMENT格式在NOW()函数上可能出现主从时间不一致,就能给面试官留下不错的印象。另外,binlog的格式选择还会影响数据恢复的灵活性:ROW格式恢复数据更精细,但需要借助工具解析。
三、主从延迟的原因与优化策略
主从延迟几乎是所有使用MySQL读写分离架构的团队都会遇到的问题。延迟通常指从库执行完主库某个事务的时间差,可以通过SHOW SLAVE STATUS中的Seconds_Behind_Master粗略判断。面试中常被问到“主从延迟怎么排查”,不要只回答“加大从库机器配置”,而要从复制链路各环节分析。
从库I/O线程拉取binlog的延迟通常较小,除非网络质量差或主库产生的binlog量极大。真正容易出问题的是SQL线程,因为它要在从库上串行重放事务,而主库是多线程并发写入。MySQL 5.6及之前的版本中,从库SQL线程只有一个,一旦遇到大事务、无主键表的批量DELETE、DDL操作等,就很容易堆积延迟。比如一个大事务在主库执行了5分钟,从库SQL线程也要差不多5分钟回放,这期间后面的所有事务都被阻塞。
优化策略分为几个层面。首先是参数层面,开启并行复制,允许从库用多个工作线程同时回放不同事务。MySQL 5.7引入基于逻辑时钟的并行复制,参数设置为:
-- 从库my.cnf配置 slave_parallel_type=LOGICAL_CLOCK slave_parallel_workers=8 slave_preserve_commit_order=ON
其次是半同步复制,它要求主库在提交事务前必须收到至少一个从库的确认,减少主从数据丢失风险,但会牺牲一定写入性能。第三种是拆分大事务,避免单事务影响过大;对于无主键表,建议补充主键,因为ROW格式下无主键表在从库回放时需要全表扫描,性能极差。
还有一个容易忽略的点:从库的配置参数要合理,比如innodb_buffer_pool_size足够大,避免频繁刷盘。如果延迟是长期存在,可以结合业务考虑是否将部分查询强制走主库,或者使用并行复制配合多线程应用。
四、GTID复制与常见故障排查
GTID(Global Transaction Identifier)是MySQL 5.6引入的全局事务标识,它给每个事务分配一个唯一的ID,格式为server_uuid:transaction_id。相比传统基于binlog文件名和偏移量的复制,GTID让主从切换、故障恢复更加简单。比如从库故障后重新搭建,不需要手动指定MASTER_LOG_FILE和MASTER_LOG_POS,靠GTID就能自动找到续传位置。
开启GTID需要在主从库都设置以下参数:
gtid_mode=ON enforce_gtid_consistency=ON log_slave_updates=ON
其中enforce_gtid_consistency保证所有事务都能被GTID标识,禁止使用临时表等违反一致性的操作。在GTID模式下,从库配置复制时使用MASTER_AUTO_POSITION=1,不再需要手动指定位置。面试时如果被问到“GTID模式与传统模式有什么区别”,可以从故障切换、自动定位、容错能力几个角度回答。
复制故障排查同样重要。当从库停止复制时,先执行SHOW SLAVE STATUS\G,重点看Slave_IO_Running和Slave_SQL_Running字段。如果IO线程不是Yes,检查网络、复制账号权限、主库防火墙;如果SQL线程不是Yes,查看Last_Error字段。常见错误包括主键冲突、删除不存在的行等。传统模式下可以执行SET GLOBAL sql_slave_skip_counter=1跳过当前错误事务,然后START SLAVE。GTID模式下跳过错误需要注入空事务:
-- 假设出错的GTID为aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:5 SET GTID_NEXT='aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:5'; BEGIN; COMMIT; SET GTID_NEXT='AUTOMATIC'; START SLAVE;
注意这里注入空事务会跳过该GTID对应的事务,需要确认该事务确实可以丢弃,否则会导致数据不一致。另外,日常运维中要定期检查主从延迟和复制错误,配合监控告警才能在面试和实际工作中都游刃有余。

