MySQL主从复制中事务执行顺序由binlog决定,从库按binlog事件顺序重放,不保证事务原子性;GTID确保不重复不丢失;大事务或从库缺失索引会导致严重延迟。

主从复制中事务的执行顺序由 binlog 决定
MySQL 主从复制不直接复制事务,而是复制 binlog 事件。事务在主库提交时,其所有 DML/DDL 操作被写入 binlog(按 commit 顺序),从库 SQL 线程按 binlog 中的顺序重放这些事件——这意味着「事务的原子性在从库不被保证」,只有「语句级或事务级的重放顺序」被保证。
关键点:
-
binlog_format必须为ROW或MIXED才能正确复制事务语义;STATEMENT模式下,某些非确定性函数(如NOW()、UUID())会导致主从数据不一致 - 即使主库上多个事务并发执行,只要它们修改不同行,
binlog仍会按 commit 时间戳排序(启用binlog_order_commits=ON,默认开启) - 从库延迟(
Seconds_Behind_Master)本质是 SQL 线程重放binlog的滞后,与事务是否“完整”无关
READ COMMITTED 隔离级别下从库读不到刚同步的事务结果
这是常见误解:从库没有自己的事务隔离控制逻辑,它只是单线程(默认)或并行地重放 binlog。即使主库用 READ COMMITTED 提交了一个事务,从库上的客户端执行 SELECT 时,看到的数据取决于 SQL 线程重放进度,而非事务隔离级别。
典型现象:
- 主库执行
INSERT INTO t VALUES (1); COMMIT;后立刻查,返回 1 - 从库同一时刻查
t,可能返回空——因为INSERT对应的binlog还没被重放 - 此时在从库执行
BEGIN; SELECT * FROM t;,结果仍为空,和隔离级别无关
这不是 bug,是异步复制的固有特性。想让从库读到最新数据,需确认 SHOW SLAVE STATUS\G 中 Seconds_Behind_Master: 0 且 Slave_SQL_Running_State 为 Slave has read all relay log。
GTID 复制下事务不会重复执行或丢失
启用 gtid_mode=ON 后,每个事务被赋予唯一标识 source_id:transaction_id,从库通过 Executed_Gtid_Set 记录已执行事务集合。SQL 线程在重放前会比对,自动跳过已执行的 GTID。
这意味着:
- 主库故障切换后,新主库可精准定位同步位点,避免漏同步或重复同步
- 从库重启或网络中断恢复后,不再依赖
Relay_Log_File和Relay_Log_Pos,而是基于 GTID 自动请求缺失事务 - 但注意:
SET SESSION gtid_next='xxx'手动指定 GTID 可能破坏一致性,生产环境禁止使用
大事务导致从库严重延迟甚至夯住
一个在主库耗时 30 秒的事务(比如 UPDATE t SET c=1 WHERE id ),在从库会被重放为单个 <code>UPDATE 语句——如果从库 IO 或 CPU 较弱,这个语句可能执行 2 分钟,期间 Seconds_Behind_Master 持续上涨,且后续所有 binlog 事件排队等待。
应对方式:
- 主库拆分大事务:用
LIMIT分批提交,例如每次更新 1000 行 +COMMIT - 从库开启并行复制(
slave_parallel_workers > 0),但仅对跨库或跨表事务有效;单表大事务仍串行 - 监控
SHOW PROCESSLIST中 SQL 线程状态,若长期卡在Updating或Writing to net,基本可判定是大事务阻塞
真正难处理的是那些「看起来小、实际慢」的事务:比如带子查询的 UPDATE 在从库因缺少索引而全表扫描——这种问题不会在主库暴露,却会让从库同步彻底卡死。










