MySQL 主从复制默认是异步复制,主库不等从库确认就提交
这是绝大多数 MySQL 主从部署的默认行为:主库执行
COMMIT后立刻返回成功,binlog 事件由后台线程异步发送给从库。这意味着网络延迟、从库繁忙或宕机时,主库完全感知不到——
INSERT返回了,但数据可能还没到从库,甚至永远丢失。
常见错误现象包括:
应用写完立即查从库,查不到刚插入的记录(SELECT返回空) 主库故障切换后,部分最近事务在从库上缺失,导致数据回退 监控显示
Seconds_Behind_Master持续增长,但主库无任何报错
这不是配置错误,而是异步机制本身的特性。想缓解,只能靠降低写入压力、优化从库 SQL 线程性能,或改用半同步。
半同步复制(semi-sync)能强制主库等待至少一个从库应答
启用
rpl_semi_sync_master_enabled=ON和
rpl_semi_sync_slave_enabled=ON后,主库在提交事务前,会等待至少一个已启用半同步的从库返回
ACK。超时(默认
rpl_semi_sync_master_timeout=10000,单位微秒)则自动降级为异步,避免阻塞业务。
关键点:
不是强一致性:仍允许主库在未收到 ACK 时降级提交,只是“尽力而为” 只保证至少一个从库落盘(relay log写入并刷盘),不保证已执行(
Exec_Master_Log_Pos不一定推进) 必须安装插件:
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'(Linux) 5.7+ 默认不带插件,8.0+ 需确认插件路径和版本兼容性
mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%'; +-------------------------------------------+------------+ | Variable_name | Value | +-------------------------------------------+------------+ | rpl_semi_sync_master_enabled | ON | | rpl_semi_sync_master_timeout | 10000000 | | rpl_semi_sync_master_trace_level | 32 | +-------------------------------------------+------------+
GTID + 基于位置的复制切换容易引发数据不一致
当主库故障、需手动提升某从库为主时,若未严格按
gtid_executed对齐,或误用
CHANGE MASTER TO ... MASTER_LOG_FILE混用 GTID 和 file-position 模式,会导致复制中断或跳过事务。
典型踩坑场景:
从库开启gtid_mode=ON,但主库是
OFF,强行配置复制会报错
ERROR 1777 (HY000)执行
RESET SLAVE ALL后忘记重置
gtid_purged,新主库的
gtid_executed缺失旧事务,从库重连后重复执行或跳过 使用
mysqlbinlog --base64-output=DECODE-ROWS -v查看 binlog 时忽略
SET @@SESSION.GTID_NEXT,误判事务边界
安全切换口诀:先查
SELECT @@global.gtid_executed;,再在目标新主库执行
SET GLOBAL gtid_purged = '...';(仅当为空时),最后让其他从库指向它并启用 GTID 自动定位。
从库延迟大时,读请求路由必须有 fallback 或超时控制
没有任何中间件能自动判断“这条 SELECT 是否涉及刚写入的数据”。如果应用层盲目把读请求发给延迟 30 秒的从库,结果就是脏读或不可见读——这不是 MySQL 的 bug,是架构设计责任。
不要依赖SHOW SLAVE STATUS的
Seconds_Behind_Master做实时判断:该值可能为 0 但实际 SQL 线程卡在锁等待 用
SELECT MASTER_POS_WAIT('mysql-bin.000001', 123456789, 2) 在主库上阻塞等待从库追上指定位置,适合关键操作后同步等待(但别在高并发接口里用)
真正可控的方式是:写后生成唯一 token(如订单号),读时带上 token 并强制走主库;或用 ProxySQL / MyCat 的 delay_threshold自动切主
最常被忽略的一点:即使启用了半同步,只要应用没做写后读一致性保障,从库延迟带来的数据不可见问题依然存在——复制机制管不到应用怎么读。
