mysql主从复制默认异步,不保证强一致性;半同步仅保障至少一份relay log落盘,仍无法确保执行完成或节点健康;真正一致性需应用层配合gtid等位读、写后读或关键路径直连主库,并辅以心跳表监控延迟。

主从复制本身不保证强一致性
MySQL 主从复制默认是异步的,MASTER 提交事务后不等待 SLAVE 应答就返回成功,这意味着从库可能滞后几毫秒到几秒甚至更久。只要没开启增强特性,SELECT 在从库读到旧数据、主从切换时丢事务、或者从库 crash 后 binlog 位点回退,都属于正常现象。
常见错误现象包括:
- 应用刚写完主库立刻查从库,查不到或查到旧值
-
SHOW SLAVE STATUS中Seconds_Behind_Master非零且持续波动 - 主库执行
DROP TABLE后,从库因 relay log 未及时重放而报错Table doesn't exist
半同步复制(semi-sync)能缓解但不解决根本问题
启用 rpl_semi_sync_master_enabled=ON 和 rpl_semi_sync_slave_enabled=ON 后,主库至少等待一个从库写入 relay log 并刷盘才返回客户端。但它只保障「至少一份副本落盘」,不保障:
- 该从库是否已执行(
Exec_Master_Log_Pos是否推进) - 该从库是否健康(它可能卡在 apply 阶段,但依然算作 semi-sync 成功)
- 网络分区时主库可能降级为异步(取决于
rpl_semi_sync_master_timeout设置)
示例配置片段:
SET GLOBAL rpl_semi_sync_master_enabled = 1; SET GLOBAL rpl_semi_sync_master_timeout = 1000000; -- 单位微秒,超时即退化
真正可控的一致性方案要靠应用层配合
没有银弹。MySQL 原生复制无法提供跨节点线性一致性,必须结合业务场景设计读写策略:
- 写后读(read-after-write):对刚写入的记录,强制走主库查询,用
SELECT ... FOR UPDATE或显式指定连接池路由规则 - 基于 GTID 的等位读:客户端拿到主库返回的
GTID_SET(如uuid:1-100),再发请求前先在从库执行SELECT WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS('uuid:1-100'),确保该从库已追平 - 延迟敏感操作绕过从库:用户登录态变更、支付结果、库存扣减等,一律不读从库
注意:WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS 会阻塞,超时抛出 ER_TIMEOUT 错误,需应用捕获并 fallback 到主库。
从库延迟监控和自动摘除不能少
仅靠 Seconds_Behind_Master 不可靠(例如从库 IO 线程卡住时该值可能为 0)。更稳妥的做法是:
- 主库定时写入心跳表(如
mysql.heartbeat),每秒更新ts字段 - 从库定期查
SELECT UNIX_TIMESTAMP() - UNIX_TIMESTAMP(ts) FROM mysql.heartbeat - 若差值 > 阈值(如 2 秒),触发告警并让负载均衡器临时剔除该从库
这个机制比依赖复制位点更贴近真实数据可见性,也避免了 relay_log_space_limit 耗尽导致 SQL 线程停摆却无感知的问题。
一致性不是开关一开就有的东西,它由主从协议、网络质量、磁盘 I/O、SQL 执行耗时、以及你敢不敢让关键路径绕过从库共同决定。最容易被忽略的是:开发往往只测单点写+单点读,却在线上面对几十个从库、不同规格、不同负载的真实拓扑。










