资讯动态

MySQL主从复制配置实战:从选型到运维的完整指南

发布时间:2026/10/13 7:59:39 来源:尧图企业网站定制
数据库跑着跑着磁盘满了或者一不留神把某张表删了才发现没有备份这种场景干运维的多少都见过。我自己的习惯是不管项目多小MySQL一上生产就先搭好主从复制。倒不是说非要追求什么高可用架构而是主库挂了能从库顶上、误操作了能从库往回找补这种兜底的安全感比事后花几个小时从备份恢复要值太多。这篇就聊聊我平时配 MySQL 主从的思路和具体操作按着我这个流程走一遍基本半小时内能拉起来一套能用的主从环境。1. 主从复制到底在解决什么问题很多人一提主从复制就想到读写分离、负载均衡这个理解不算错但有点想多了。主从复制最核心的价值是数据冗余和高可用兜底。拿我维护过的一个电商系统举例主库扛着所有的读写压力某天凌晨磁盘被慢查询日志写满了数据库直接只读。如果没有从库整个业务就瘫在那里等我们手动清日志、修磁盘。但因为提前配了主从从库在另一台机器上数据是全的直接把从库提为主库切一个虚拟 IP 过去业务中断时间控制在五分钟以内。再说数据备份。虽然可以用 mysqldump 做定时全量备份但备份窗口和备份频率总有限制万一在两次备份之间出现误删数据从库就能派上大用场。从库上的 binlog 还保留着完整的时间线可以用工具分析出误操作之前的准确位置做精准恢复。这比从全量备份里一点点追 binlog 要快得多。主从复制在架构上的作用可以总结成三点读写分离的底座主库只写从库分担读流量前提是业务能接受读延迟。容灾切换的候选节点主库故障时从库可以作为新主库顶上去。备份与分析的隔离节点在从库上跑大查询、做备份不干扰主库性能。理解了这个配置的时候心里就有数了主从不是简单搭起来就行还要考虑往后怎么切换、怎么监控、怎么防脑裂。所以搭建时每一步都要考虑后续运维的便利性。2. 配置前需要想清楚的几个关键点2.1 复制方式选哪种MySQL 主从复制主流的方式有三种异步复制、半同步复制、组复制Group Replication。平时用得最多的是异步复制和半同步复制。异步复制是所有复制方式的基座。主库提交事务后不等待从库确认直接返回客户端成功从库通过 IO 线程拉取主库 binlog再通过 SQL 线程应用到自身。优点是主库性能几乎不受影响缺点是从库延迟可能比较大极端情况下主库宕机而 binlog 还没同步过去会有数据丢失。半同步复制是在异步之上加了一层保障。主库在提交事务后要等至少一个从库确认收到了 binlog 才返回成功。这样能最大限度减少数据丢失但会引入一定的延迟因为主库要等网络往返。组复制就更复杂一点适合对数据一致性要求极高的场景通常要配合 MySQL 集群方案来用架构复杂度高了不少一般的小团队不太建议一开始就上。我个人在中小型项目里推荐半同步复制。它既保证了数据基本不丢性能代价又相对可控。后面我会把半同步的配置方法一起写出来你可以在异步复制搭好之后直接叠加。2.2 binlog 格式怎么选binlog 是主从复制的数据源头它的格式直接决定了复制的内容和兼容性。STATEMENT记录的是 SQL 语句本身。优点是日志量小缺点是有很多函数和语句在不同机器上执行结果可能不一致比如 NOW()、UUID() 这类容易造成主从数据不一致。ROW记录的是每一行数据的变更前后镜像。优点是复制最精确几乎不会因为语句执行环境不同而出现偏差缺点是 binlog 量会大很多尤其大事务更新多行时尤为明显。MIXED混合模式。MySQL 会根据语句内容自动选择用 STATEMENT 还是 ROW。我现在一律用 ROW 模式。早期踩过 STATEMENT 模式的坑一条带 LIMIT 的 UPDATE 语句在主从库上因为索引选择不同而影响了不同的行数据就直接对不上了。从那以后就学乖了数据一致性比那点日志量重要得多。2.3 主库和从库版本怎么搭配这是一个很现实的问题。如果主库是 8.0从库是 5.7复制很可能失败因为 binlog 的格式和事务的元数据在版本之间不兼容。我的建议是主库版本高于或等于从库版本尽量用同一个小版本。比如主库 8.0.32从库最好也是 8.0.32。如果实在版本不同至少保证从库版本不高于主库并且提前在测试环境验证复制是否正常。还有一条如果从库已经跑了一段时间的业务数据直接用 binlog 复制会出问题因为从库的已有数据是“无源之水”binlog 里没有对应的起始点。这种情况下要先做一次全量初始化把主库的数据整体搬到从库再从起点开始复制。3. 环境和准备工作动手之前先把环境理清楚。我用一套最简单的架构来演示一台主库、一台从库都在同一个内网。版本是 MySQL 8.0.32操作系统是 CentOS 7。先说账号权限。主库上需要创建一个专门的复制账号权限只需要REPLICATION SLAVE不要给它其他多余的权限。安全习惯很重要复制账号不要用 root密码也尽量复杂一点。再说防火墙。主库的 3306 端口要能从从库访问。很多主从不通的问题最后排查下来都是防火墙、安全组这一类网络层面的问题。如果你在云上注意安全组的入方向规则如果你在自己的机房检查 iptables 和 firewalld。再提醒一个细节从库机器的时间尽量和主库保持一致最好都配了 NTP 时间同步。复制相关的 GTID 和日志定位都依赖时间戳时间差太大会对问题排查造成干扰。还需要提前确认主库的 server-id 和从库的 server-id 不能一样。默认情况下 MySQL 的 server-id 都是 1如果不改从库连接主库后复制会直接报错冲突。这个坑我见了很多次每次都要在检查清单里反复确认。3.1 主库的配置文件调整主库这边需要打开 binlog、设置 server-id、选定 binlog 格式。修改my.cnf在[mysqld]段落下加配置[mysqld] server-id 1 log_bin /var/log/mysql/mysql-bin binlog_format ROW expire_logs_days 15 max_binlog_size 256M binlog_do_db your_db解释一下每项的意思server-id必须唯一主库我习惯用 1。log_bin指定 binlog 的文件路径和前缀名。binlog_format前面说过用 ROW。expire_logs_daysbinlog 保留天数太短会丢历史日志导致从库追不上太长会撑爆磁盘。15 天是我常用的值依据是备份周期和从库延迟的容忍度。max_binlog_size单个 binlog 文件的最大体积超过会自动滚动生成新文件。binlog_do_db指定只记录某个库的变更。这个配置要谨慎用因为如果后续切换了业务库或者业务上新增了数据库容易漏记。现在很多场景下我更倾向于不设置这个参数全库都记录 binlog反而省心。改完配置要重启 MySQLsystemctl restart mysqld重启后登录主库验证 binlog 是否开启SHOW VARIABLES LIKE log_bin; SHOW MASTER STATUS;如果能看到File和Position字段有值说明 binlog 已经在正常写了。3.2 从库的配置文件调整从库这边要设置的参数跟主库有些差异。它不需要开 binlog 做复制源但如果这个从库以后还可能继续向下级联复制或者需要基于它再做从库那最好也打开 binlog。我这里打开因为后续可能用它做二级从库或者备份节点。[mysqld] server-id 2 log_bin /var/log/mysql/mysql-bin binlog_format ROW relay_log /var/log/mysql/mysql-relay-bin read_only ONrelay_log是中继日志的路径从库的 IO 线程把主库 binlog 拉到本地后先写进中继日志再由 SQL 线程读取和应用。read_only是只读参数设置为 ON 后普通账号不能通过 SQL 语句写入数据但复制线程不受影响。这个参数很重要防止因为误操作而从库被写入了与主库不一致的数据。同样需要重启从库的 MySQL 让配置生效。4. 全量初始化从库数据配置好两边参数只是第一步。接下来要解决“从库里没有主库当前数据”的问题。最稳妥的方式是用mysqldump在主库上导出一个带--master-data参数的全量备份。我自己习惯用--single-transaction --set-gtid-purgedOFF这两个参数配合mysqldump -u root -p --single-transaction --master-data2 --set-gtid-purgedOFF --default-character-setutf8mb4 your_db your_db.sql解释一下这几个参数的用意--single-transaction使用一致性快照进行导出不加全局读锁不影响在线业务写入。--master-data2在导出的 SQL 文件头部记录主库 binlog 的当前文件位置注释形式存在方便后面明确复制起点。--set-gtid-purgedOFF如果使用 GTID 复制需要根据情况设置。如果不用 GTID这个参数可以忽略。有人说是不是必须用--master-data如果你打算记录复制起点这个参数就很重要。比如主库当前的 binlog 文件是mysql-bin.000015位置是12345导出的文件头会自动写明CHANGE MASTER TO MASTER_LOG_FILEmysql-bin.000015, MASTER_LOG_POS12345;。这样从库导入数据后可以直接用这个坐标去主库拉取后续 binlog实现平滑衔接。把导出文件传到从库后在从库上导入mysql -u root -p your_db your_db.sql导入过程中如果从库已经有数据最好先清空目标库或者直接 drop 掉重建避免导入报错。如果你要复制的库数量比较多也可以使用全库导出不用指定单个库。操作上是一样的只是文件更大耗时更长。还有人问能不能直接用 xtrabackup 做物理备份。能而且在大数据量场景下比 mysqldump 快很多。xtrabackup 可以在不锁表的情况下做物理备份备份完成后把数据目录整体同步到从库。但它的安装和使用相对复杂一些数据量在几十 GB 以内我其实更推荐 mysqldump简单可靠恢复逻辑也清晰。5. 在主库上创建复制账号复制账号是主从之间身份验证的关键一环。在主库执行CREATE USER sync_user192.168.1.20 IDENTIFIED BY YourStrongPass123!; GRANT REPLICATION SLAVE ON *.* TO sync_user192.168.1.20; FLUSH PRIVILEGES;注意这里的主机限定我写的是从库的 IP不要用%通配。主机限定的意义在于即使密码泄露其他机器也无法利用这个账号连接主库。账号建好后从库会用这个账号去连接主库拉取 binlog复制链路就建立起来了。6. 在从库上配置复制链路从库上执行CHANGE MASTER语句把复制源的信息告诉 MySQL。CHANGE MASTER TO MASTER_HOST192.168.1.10, MASTER_USERsync_user, MASTER_PASSWORDYourStrongPass123!, MASTER_LOG_FILEmysql-bin.000015, MASTER_LOG_POS12345;如果前面 mysqldump 时用了--master-data2导出的文件头已经给出了正确的文件号和位置直接替换上面的值就行。如果没记录位置也可以在主库上执行SHOW MASTER STATUS查到当前的 binlog 文件号和位置但这样有风险从 dump 开始到查位置之间主库如果新写入了数据而 dump 的数据集里又恰好包含这些新事务就可能重复执行或者数据不一致。接着启动复制START SLAVE;启动后查看复制状态SHOW SLAVE STATUS\G;重点关注四行Slave_IO_Running: YesSlave_SQL_Running: YesSeconds_Behind_Master: 0Last_IO_Error和Last_SQL_Error为空IO 线程负责从主库拉 binlogSQL 线程负责应用中继日志。两个线程都显示 Yes 且错误信息为空说明链路基本通了。如果Slave_IO_Running是 Connection多半是网络不通、账号密码错误或主库端口被防火墙拦截。如果Slave_SQL_Running是 No一般是 SQL 应用过程中跟从库已有的数据冲突比如主键重复这种情况就要根据报错信息具体定位。Seconds_Behind_Master表示从库落后的秒数。刚启动时可能不是 0过一会儿就追上来了。如果这个值持续很大甚至一直增长说明从库的 SQL 线程应用能力跟不上主库的写入速度。常见的原因是主库执行了一些大事务比如一次性更新几百万行或者从库硬件配置比主库差太多。如果在生产环境遇到复制中断赶紧通过SHOW SLAVE STATUS看错误信息然后决定是直接跳过错误还是重新设置复制位置。这里有一个重要原则不要盲目跳到下一个事务跳过错误一定要先搞清楚错误原因否则可能掩盖掉真正的数据问题。7. 升级到半同步复制异步复制配好后如果觉得数据安全性不够可以叠加半同步。MySQL 8.0 里半同步插件是内置的只要安装启用就行。先确定动态库位置SHOW VARIABLES LIKE plugin_dir;然后分别在主库和从库上安装插件INSTALL PLUGIN rpl_semi_sync_source SONAME semisync_source.so; INSTALL PLUGIN rpl_semi_sync_replica SONAME semisync_replica.so;注意 8.0 里插件名发生了变化。旧版本叫rpl_semi_sync_master和rpl_semi_sync_slave新版本改成了 source 和 replica 的命名体系。如果你用的是 5.7需要按旧名安装。主库启用半同步并设置超时SET GLOBAL rpl_semi_sync_source_enabled 1; SET GLOBAL rpl_semi_sync_source_timeout 3000;rpl_semi_sync_source_timeout表示主库在等待从库确认时最多等多少毫秒。超过这个时间后主库会自动降级为异步复制不会让业务一直卡死。我这里设为 3000 毫秒即 3 秒。从库启用半同步SET GLOBAL rpl_semi_sync_replica_enabled 1;然后需要重启复制才能让半同步生效STOP SLAVE; START SLAVE;验证半同步是否生效可以看SHOW STATUS LIKE Rpl_semi_sync_source_status;如果结果是ON说明半同步复制在工作。这里有个容易踩的坑半同步复制的超时时间不要设太长。如果设成 30 秒从库一旦宕机主库每个事务都要等 30 秒才能返回成功业务直接没法看。3 秒是我的默认值特殊情况可以根据主从之间的网络状况微调但建议不要超过 5 秒。8. 验证主从复制是否真的正常工作复制的验证不能只看SHOW SLAVE STATUS里的Running字段还要实际验证数据的一致性。我在主库执行一条插入语句INSERT INTO your_db.sync_test (user_name, created_at) VALUES (replica_test, NOW());然后在从库查询SELECT * FROM your_db.sync_test WHERE user_name replica_test;能看到数据说明复制链路通了。接下来再验证更新和删除UPDATE your_db.sync_test SET user_name replica_test_2 WHERE id 1; DELETE FROM your_db.sync_test WHERE user_name replica_test_2;每一步都在从库上确认结果。这里我说一下为什么验证很重要我见过太多案例状态显示 Yes但实际行数对不上。多半是因为复制初始化的数据快照和 binlog 起点没有对齐导致中途丢了一些事务。推荐在搭建完成后再做一个更严谨的校验操作分别在主库和从库上执行CHECKSUM TABLE对比同一张表的校验结果是否一致。这是一张大表的话可能需要一些时间但能非常直接地暴露数据不一致问题。日常监控里也要定期抽查几张核心表的行数用定时脚本对比主从两侧。写入频率高的系统数据行数只要持续一致基本可以说明复制是健康的。9. 日常运维里常见的问题和处理手段主从搭建只是开始后面日常运维才是重头。整理几个高频问题和对应处理思路。9.1 IO 线程连不上主库Slave_IO_Running显示 Connecting 或 No。大概率是网络不通、账号权限错、密码错、主库端口没监听。排查步骤从从库上用telnet或nc测试主库 3306 端口是否可达。用复制的账号在从库上手动连接主库验证认证是否通过。检查主库的bind-address配置确认允许从库所在 IP 访问。检查防火墙和安全组规则。我遇到过一种很隐蔽的情况主库本身没问题但中间有一层云安全组规则限制了内网 IP 段的互访排查了很久才发现是安全组只开了外网访问规则内网段反而挡掉了。9.2 SQL 线程报错主键冲突Last_SQL_Error提示 duplicate key。通常是因为从库上已经有这条数据但是 binlog 里的事务又要插入一次。出现这种情况要特别小心它往往意味着从库之前被人为写入过数据或者初始化数据不完整导致 SQL 线程应用事务时撞到已有的主键。处理思路有两种。第一种是用STOP SLAVE暂停手动删除从库上的冲突数据然后重新START SLAVE。第二种如果确认只是单条错误并且可以跳过可以用SET GLOBAL sql_slave_skip_counter 1跳过一个事务。但老实说这个操作要谨慎不可滥用。9.3 从库延迟持续增长Seconds_Behind_Master持续增长说明 SQL 线程应用速度赶不上主库写入速度。要么是主库写入量太大要么是 SQL 线程被大事务卡住了。常用解决手段检查是否有大事务在主库执行比如大批量更新binlog 里一个事务包含几百万行变更从库要一条条应用到本地必然慢。检查从库磁盘 IO 是否成为瓶颈方向上是保证从库磁盘性能不低于主库。检查从库上是否有慢查询占用了大量资源。如果 binlog 里有大事务尽量把 SQL 拆成多批小事务执行降低单事务的复制压力。9.4 主从数据不一致MySQL 主从复制不能百分之百保证数据一致特别是复杂 SQL 和特殊函数场景下可能主从执行结果不同。如果是 ROW 格式这种情况少了很多但也不能完全排除。我建议每两周对核心表做一次CHECKSUM TABLE也可以使用开源工具进行更全面的比对和修复。发现不一致时优先修复主库数据再重建对应表的从库副本而不是直接在从库上改数据。因为从库改完下一次 binlog 过来可能又把不一致带回来了。9.5 从库误写入数据前面配置里设置了read_only ON可以在很大程度上防止误写入。但注意read_only只限制普通账号拥有 SUPER 权限的账号依然可以写入。日常运维里尽量别用 root 去操作从库业务表。如果想做到彻底只读可以再开启super_read_only ON这样连 SUPER 权限的账号都无法写入。复制线程本身不受影响。这是生产环境强烈建议开启的参数。我踩过这么一次坑某次手动在主库执行修复脚本结果脚本连的是从库直接往从库写了一条数据。从那以后super_read_only成了我所有从库的必配项。10. MySQL 8.0 下用 GTID 复制替代传统位点复制前面讲的复制方式是基于 binlog 文件的文件名和位置来定位复制进度。这种方式的缺点是位置信息是相对的切换或故障恢复时要重新确认文件号和位置。如果使用 GTID就方便很多。GTID 是一个全局事务标识符每个事务在主库执行时都会分配一个唯一的 GTID复制时根据 GTID 即可确认哪些事务已经执行过、哪些还没有。这样从库自动跳过已经应用过的事务不会重复执行。启用 GTID 需要修改配置[mysqld] gtid_mode ON enforce_gtid_consistency ON修改后重启 MySQL然后从库上的CHANGE MASTER语句可以简化CHANGE MASTER TO MASTER_HOST192.168.1.10, MASTER_USERsync_user, MASTER_PASSWORDYourStrongPass123!, MASTER_AUTO_POSITION 1;GTID 模式下不再需要指定 binlog 文件和位置从库会自动根据 GTID 集合决定从哪里开始拉取 binlog这比传统位点定位方式省心不少。设置MASTER_AUTO_POSITION 1后启动复制START SLAVE;GTID 模式对故障切换特别友好。主库宕机时直接把从库提升为主库其他从库重新指向它时不需要手工计算二进制日志位置直接MASTER_AUTO_POSITION 1即可自动从合适的 GTID 位置恢复复制。如果你打算以后做一主多从或者主从切换强烈建议直接用 GTID。不过 GTID 也有一些限制比如不支持非事务引擎的复制有一些历史遗留的 SQL 写法可能会触发 GTID 一致性检查报错。但现在的业务基本都在用 InnoDB影响不大。11. 从库如何实现安全的主从切换当主库故障或者要做计划内维护时把从库提升为新主库是一个标准操作。切换前需要做好充分的检查否则容易造成业务中断或数据丢失。切换流程我总结为四步。第一步确认从库数据是否追平主库。在主库上查看当前 binlog 坐标再到从库上对比或者通过Seconds_Behind_Master判断是否落后了很多。第二步在主库上锁定写操作确保没有新事务进来。如果是计划内维护可以把业务停写如果是主库故障能做的有限尽量让业务切换到只读模式。第三步在从库上停止复制线程STOP SLAVE; RESET SLAVE ALL;STOP SLAVE让复制停止在当前状态RESET SLAVE ALL清除与旧主库的连接信息使从库成为一个独立的数据库实例。第四步把从库上的super_read_only关闭开放写权限。SET GLOBAL super_read_only OFF;如果原主库只是维护而不是彻底损坏后续可以把它作为新主库的从库重新挂上来只需要在新主库上创建复制账号再按前文步骤配置复制即可。这里有一个容易忽略的操作切换完成后原主库和现主库之间要避免同时写同一份数据也就是所谓的双主脑裂。最稳妥的方案是在应用层或者中间件层控制写流量只指向新主库别用双写方式让两边同时接受写入否则数据冲突之后很难理得清。12. 结合实际场景复盘一次配置过程讲一个我最近帮某公司搭的实例。这套架构是主库一台高配物理机从库两台云主机版本都是 8.0.28。业务是典型的订单系统每天写入量中等高峰时有几百 TPS。配置时我主库开启了 binlog格式 ROW保留 15 天从库设置read_only和super_read_only防止误写。数据初始化用的mysqldump由于数据量在几十个 GB全量导出导入大概花了二十多分钟。然后两台从库分别配置CHANGE MASTER指定了同一个 binlog 坐标启动复制。这个过程里出现一个小问题第二台从库启动后Slave_IO_Running显示 Connecting查了一圈发现安全组只放行了第一台从库的 IP第二台的 IP 没加进去。加上之后立刻恢复。后面业务高峰期观察了两个星期主从延迟基本保持在 1 秒以内。中途有一次主库执行了一个大的批量清理任务更新了上百万行数据从库延迟瞬间飙到几十秒。不过事务结束后延迟慢慢追了回来。这也很正常大事务场景下延迟一定会起来关键是看能否在一个合理的时间窗口内追平。这次配置中我还是用了 GTID 模式。因为后续计划做读写分离和主从自动切换用 GTID 会省掉很多麻烦。复制链路稳定之后我在从库上还挂了一些分析类的查询比如订单报表、运营数据统计压力都从主库剥离出去了。13. 那些没写在文档里的经验细节最后补充几个教科书上不写、但实际用起来很关键的点。第一主从之间的网络延迟要定期监控。内网延迟正常情况下应该在 1 毫秒以内如果超过 10 毫秒半同步复制的主库写性能会明显下降。网络抖动是复制延迟的一大来源而且不容易被发现需要从网络监控指标里去看。第二binlog 文件不要无限增大。max_binlog_size我习惯设成 128M 到 256M太小会导致频繁切换文件增加 IO 开销太大会让单个日志文件恢复起来困难。第三中继日志也可能异常膨胀。如果从库 SQL 线程应用能力跟不上中继日志会一直堆积占用磁盘空间。平时要关注从库的 relay log 目录磁盘占用比例。第四备份计划依然要做。主从复制是冗余方案不是备份方案。万一整个机房都出问题或者有人恶意的执行了 DROP 操作复制会把删除操作也复制到从库。所以全量备份和 binlog 归档一个都不能少。第五监控项要重点盯Slave_IO_Running、Slave_SQL_Running、Seconds_Behind_Master、中继日志目录磁盘占用、主库 binlog 目录磁盘占用。这五项基本能覆盖主从健康度的核心指标建议接入到告警系统。我在实际维护中是把告警阈值设置为从库延迟超过 30 秒持续 5 分钟就触发告警任何一个线程不是 Yes 状态就立即告警。这样能第一时间发现问题而不是等到业务故障了才开始排查。MySQL 主从配置本身并不复杂复杂的是配合业务场景做正确的取舍和后续的稳定运维。希望这套操作流程能帮你少走一些弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑