当业务量增长到一定程度,单台MySQL服务器往往同时面临两个问题:一是读写压力大导致响应变慢,二是一旦服务器故障数据就可能丢失。主从复制(Replication)架构正是解决这两类问题的经典方案。主库(Master)负责处理写请求,从库(Slave)通过异步复制主库的数据变更来保持数据一致,读请求可以分发到从库上执行,从而实现读写分离与数据冗余。本文将从原理到实操,完整讲解如何搭建一套MySQL主从复制架构。
一、主从复制的底层原理
MySQL的主从复制基于二进制日志(Binary Log,简称binlog)实现。主库在执行完每一个写操作后,会将变更以事件的形式记录到binlog中,这个过程是主库单方面完成的,从库并不知道谁会来读取它的日志。
从库的复制工作由两个线程协作完成。第一个是IO线程,它在启动复制后会连接主库,主库随后创建一个Dump线程,将binlog中的事件持续推送给IO线程。IO线程收到事件后,将它们写入本地的中继日志(Relay Log)。第二个是SQL线程,它负责读取中继日志中的事件并在从库本地重放执行,这样从库的数据就与主库保持一致了。
理解这个过程非常重要,因为它直接关系到后续的故障排查。例如著名的Seconds_Behind_Master指标,本质上反映的就是SQL线程重放的滞后程度;如果IO线程断了,数据就会完全停止同步。整个复制默认是异步的,主库提交事务后不等待从库确认,因此主库性能不受影响,但极端情况下主库宕机可能丢失少量未同步的数据。
二、搭建主从复制的完整步骤
下面以两台服务器为例,环境为Linux加MySQL 8.0,主库IP为192.168.0.1,从库IP为192.168.0.2。整个过程分为主库配置、创建复制账号、从库配置三个阶段。
1. 配置主库
编辑主库的配置文件,Linux下通常是/etc/my.cnf,在[mysqld]段中添加如下配置:
# 主库配置,server-id必须在整个复制集群中唯一 [mysqld] server-id=1 log-bin=mysql-bin binlog_format=ROW # 8.0默认开启,如果是旧版本需要手动开启gtid gtid_mode=ON enforce_gtid_consistency=ON
其中server-id是复制集群中每个节点的唯一标识,取值1到2的32次方减1之间,绝对不能重复。log-bin开启二进制日志,binlog_format推荐使用ROW格式,它记录的是每行数据的实际变更,比STATEMENT格式更安全,能避免一些函数(如NOW())在从库执行结果不一致的问题。
2. 创建复制专用账号
从库连接主库需要专门的账号,出于安全考虑不要使用root。登录主库执行:
-- 创建复制账号,限制只能从从库IP访问 CREATE USER 'repl'@'192.168.0.2' IDENTIFIED BY 'Repl@123456'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.0.2'; FLUSH PRIVILEGES;
3. 配置从库并启动复制
编辑从库配置文件,设置不同的server-id,然后重启两台MySQL使配置生效:
# 从库配置 [mysqld] server-id=2 read_only=ON # 开启GTID便于自动定位复制位点 gtid_mode=ON enforce_gtid_consistency=ON
在从库执行以下命令建立与主库的连接并开启复制:
CHANGE REPLICATION SOURCE TO SOURCE_HOST='192.168.0.1', SOURCE_USER='repl', SOURCE_PASSWORD='Repl@123456', SOURCE_AUTO_POSITION=1; START REPLICA; -- 查看复制状态 SHOW REPLICA STATUS\G
重点观察输出中的两项:Replica_IO_Running和Replica_SQL_Running都必须为Yes,Seconds_Behind_Master为0表示同步正常。如果使用的是MySQL 8.0.22之前的版本,命令需要换成CHANGE MASTER TO和START SLAVE。
三、为什么推荐使用GTID模式
传统的复制方式需要手动指定MASTER_LOG_FILE和MASTER_LOG_POS,也就是告诉从库从binlog的哪个文件哪个位置开始复制。这种方式有个明显缺陷:主库宕机后切换新主库时,管理员需要人工计算各从库的复制位点,容易出错且操作复杂。
GTID(全局事务标识符)为每个事务分配一个集群范围内唯一的编号,格式为server_uuid:事务序号,例如3E11FA47-71CA-11E1-9E33-C80AA9429562:23。开启GTID后,从库只需要设置SOURCE_AUTO_POSITION=1,它会自动向主库上报自己已执行的事务集合,主库据此发送缺失的事务,无需人工干预文件和位点。
GTID模式还带来两个好处:一是故障切换更简单,搭建主从切换工具(如MHA、Orchestrator)时配置大幅简化;二是可以通过gtid_executed集合快速判断某个事务是否已经在从库执行过,避免重复执行导致的数据冲突。需要注意,开启GTID后要保证enforce_gtid_consistency处于开启状态,且像CREATE TABLE ... SELECT这类不支持GTID的语句会直接报错。
四、实现读写分离的常见方式
复制搭建完成后,读写分离的实现方式主要有三种,各有适用场景。
第一种是代码层实现。在应用程序中配置两个数据源,写操作走主库,读操作走从库。这种方式最直观,可控性强,但业务代码与数据库架构产生了耦合,后期增加从库需要改代码重新发布。
第二种是使用中间件,例如ProxySQL、MyCat或ShardingSphere-Proxy。应用程序只连接中间件,中间件根据SQL语句类型自动路由,SELECT发送到从库,INSERT、UPDATE、DELETE发送到主库。以ProxySQL为例,它还支持读写比重的加权分配、慢查询统计和连接池管理,是生产环境的主流选择。
-- ProxySQL中配置从库读组的示例 INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup) VALUES (10, 20); -- 将主库加入写组10,从库加入读组20
第三种是框架内置支持,例如Java生态中的ShardingSphere-JDBC,以客户端Jar包的形式嵌入应用,无需额外部署中间件,性能损耗更小。选择哪种方式取决于团队技术栈和运维能力,中小项目用代码层或JDBC方式即可,大型集群建议用独立中间件统一管理。
五、常见问题排查与运维要点
1. 主从延迟
主从延迟是最常见的问题,表现为Seconds_Behind_Master持续增大。主要原因包括:从库单线程重放跟不上主库的写入速度(大事务尤其明显)、从库硬件配置过低、从库上跑了大量读查询占用资源。MySQL从5.7开始支持并行复制,在从库设置replica_parallel_workers=4可以让SQL线程使用多个工作线程并行重放事务,对缓解延迟效果显著。此外应避免在主库执行一次性更新百万行的大事务,可拆分为小批量执行。
2. 复制中断
如果Replica_SQL_Running变为No,通常是从库执行事务时报错,比如主从数据本来就不一致,或从库被误写入数据。处理流程是先执行SHOW REPLICA STATUS\G查看Last_SQL_Error定位具体错误。对于可跳过的错误,可以执行:
STOP REPLICA; SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START REPLICA;
如果是GTID模式,则需要注入一个空事务来跳过:
STOP REPLICA; SET GTID_NEXT='出错的GTID编号'; BEGIN; COMMIT; SET GTID_NEXT='AUTOMATIC'; START REPLICA;
跳过错误只是应急手段,跳过后务必核对主从数据是否一致,可用pt-table-checksum工具校验,发现差异后用pt-table-sync修复。更根本的做法是保持从库read_only=ON,禁止业务直接写从库。
3. 日常监控
生产环境建议持续监控复制状态,重点指标包括两个复制线程的运行状态、延迟秒数、binlog文件增长速度。可以编写脚本定时查询SHOW REPLICA STATUS,发现线程停止或延迟超过阈值(如60秒)立即告警。同时定期备份依然不可省略,主从复制提供的是冗余和高可用能力,但误删表这类逻辑错误会被原样复制到从库,只有备份才是最后防线。
总结
MySQL主从复制是构建高可用、可扩展数据库体系的基石。核心流程是主库记录binlog,从库通过IO线程和SQL线程拉取并重放日志;搭建时注意server-id唯一、使用GTID简化管理;读写分离可通过代码、中间件或框架实现;运维上重点关注主从延迟与复制中断,并坚持定期备份。掌握这套架构后,还可以进一步扩展到半同步复制、MGR集群等更强的数据安全方案。