学完这篇,你能从零搭起一套 MySQL 主从复制,并知道半同步、GTID、并行复制这三个选项各自解决什么问题、该怎么开、开了之后看哪里确认它真的生效。
第一步:搞清复制的三个线程在干什么
主库上有个 binlog dump 线程,负责把 binlog 事件推给从库;从库上有个 IO 线程,收下来先写进本地 relay log;再由 SQL 线程(回放线程) 读 relay log 重放成数据变更。所以延迟 = 网络传输 + relay log 落盘 + SQL 回放,其中绝大多数情况瓶颈在最后一段回放,这也是并行复制要解决的问题。
注意:复制是异步的,主库提交成功不代表从库有了。主库宕机时未传过去的 binlog 事件就是丢的数据,这就是后面要上半同步的原因。
第二步:配好两库的基础参数
两台机器(或两个实例)的 my.cnf:
# 主库
server_id = 1
log_bin = mysql-bin
binlog_format = ROW
# 从库
server_id = 2
log_bin = mysql-bin
binlog_format = ROW
read_only = ON
super_read_only = ON
server_id 必须全局唯一,重了会直接断复制。改完重启,主库执行 SHOW MASTER STATUS; 能看到文件名和位点就算成功。
注意:从库也建议开 log_bin,一是方便级联复制,二是开了 GTID 后从库必须能写 binlog,否则数据变更无法记入自己的 GTID 集合。
第三步:建复制账号、做全量备份
CREATE USER 'repl'@'%' IDENTIFIED BY 'StrongPass_2024';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
主库导一份带位点的备份:
mysqldump -uroot -p --single-transaction --source-data=2 \
--all-databases > /tmp/full.sql
--source-data=2(老版本叫 --master-data=2)会把当时的 binlog 位点或 GTID 以注释形式写进备份文件,从库导入后照着它配起点即可。导入:mysql -uroot -p < /tmp/full.sql。
第四步:建立复制链路
推荐直接用 GTID,不用手算位点:
-- 主库 my.cnf 加:gtid_mode=ON enforce_gtid_consistency=ON
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='10.0.0.11', SOURCE_PORT=3306,
SOURCE_USER='repl', SOURCE_PASSWORD='StrongPass_2024',
SOURCE_AUTO_POSITION=1;
START REPLICA;
SHOW REPLICA STATUS\G
看到 Replica_IO_Running: Yes、Replica_SQL_Running: Yes、Retrieved_Gtid_Set 在增长,就通了(5.7 及以前叫 CHANGE MASTER TO / SHOW SLAVE STATUS)。
注意:GTID 模式下 sql_slave_skip_counter 是失效的,跳过一条坏事务要手动注入空事务:SET GTID_NEXT='uuid:序号'; BEGIN; COMMIT; SET GTID_NEXT='AUTOMATIC';。另外 SECONDS_BEHIND_MASTER 并不准,长事务期间它会显示 0,要准确判断延迟建议用 pt-heartbeat 或对比 performance_schema 里的时间戳。
第五步:上半同步,减少丢数据窗口
-- 主从都装插件
INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so';
INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so';
-- 从库
SET GLOBAL rpl_semi_sync_replica_enabled = ON;
-- 主库
SET GLOBAL rpl_semi_sync_source_enabled = ON;
SET GLOBAL rpl_semi_sync_source_timeout = 1000; -- 毫秒
开启后主库提交事务会等至少一个从库回 ACK 再返回,跑 SHOW STATUS LIKE 'Rpl_semi_sync_source_status'; 看到 ON 才算生效。
注意:超时(默认 10000ms)后主库会自动退化成异步,业务无感知但保护已经没了。Rpl_semi_sync_source_no_tx 持续增长就说明一直在退化,通常是网络抖动或从库卡回放。另外半同步至少要两个从库才有容错空间,单从库挂掉就等于没有保护。
第六步:开并行复制,把回放瓶颈拉开
replica_parallel_workers = 8
replica_parallel_type = LOGICAL_CLOCK
replica_preserve_commit_order = ON
主库侧配合 binlog_transaction_dependency_tracking = WRITESET(8.0 默认值),让 binlog 记录事务依赖,从库才能安全地并行回放。
注意:并行度受主库并发度限制——主库单线程写入,从库开 8 个 worker 也不会跑到 8 倍。想看实际用了几个线程,查 performance_schema.replication_applier_status_by_worker。replica_preserve_commit_order 必须为 ON,否则并行回放后从库数据和主库提交顺序不一致,切换时会出问题。
第七步:日常巡检看这几个点
SHOW REPLICA STATUS 的两个 Yes 和 Last_Error
Seconds_Behind_Source 趋势(只做参考)
- 半同步状态
Rpl_semi_sync_source_status 与 no_tx 计数
- worker 线程是否都在 Running
- 主库
SHOW BINARY LOGS 有没有因为从库太慢而堆积
小结
复制链路就是“binlog → relay log → 回放”三段;GTID 让你不用记位点、切换更省事,是首选方式;半同步把丢数据窗口从“可能几千条”压到“最多一个事务”,但会退化,必须监控;并行复制只在从库侧生效,主库并发度高才有明显收益,replica_preserve_commit_order 不能关。落地顺序建议:先 GTID 跑通以获取基础链路,再加半同步,最后按回放延迟决定要不要开并行。