mysql如何防止主从数据不一致_复制一致性保证

1次阅读

mysql主从复制默认不保证强一致性,因其采用异步机制:主库提交事务后才发送binlog至从库,且从库sql线程回放存在延迟,导致“先写后读”可能读到旧值或空值。

mysql如何防止主从数据不一致_复制一致性保证

主从复制为什么默认不保证强一致性

MySQL 主从复制是异步的,binlog 写入主库后才发给从库,中间存在时间差;从库 SQL Thread 回放也可能延迟。这意味着:主库提交事务后立刻读从库,很可能读不到最新数据,甚至读到旧值或空值。这不是 bug,而是设计取舍——异步复制优先保障写入性能,而非实时一致性。

常见错误现象包括:select 刚插入的数据查不到、分页错乱、状态变更未同步就触发下游逻辑。

  • 主库 INSERT 后立即在从库 SELECT,返回空
  • 应用层“先写后读”场景(如注册后跳转个人页)出现 404 或旧信息
  • SHOW SLAVE STATUSSeconds_Behind_Master 持续 > 0

用 semi-sync 复制降低不一致窗口

半同步(semi-sync)要求至少一个从库将 binlog 写入 relay log 并刷盘后,主库才返回成功。它不能消除延迟,但能避免“主库提交了,但从库一条都没收到”的极端不一致。

启用前需确认:从库已安装 rpl_semi_sync_slave 插件,主库安装 rpl_semi_sync_master;且 timeout 参数不宜设为 0(否则主库会卡死)。

  • 主库执行:SET GLOBAL rpl_semi_sync_master_enabled = 1;
  • 从库执行:SET GLOBAL rpl_semi_sync_slave_enabled = 1;
  • 检查状态:SHOW VARIABLES LIKE 'rpl_semi_sync%';SHOW STATUS LIKE 'Rpl_semi_sync%';
  • 注意:rpl_semi_sync_master_timeout 建议设为 1000–10000(毫秒),超时后自动退化为异步

应用层读写分离必须做一致性路由

如果业务代码直接连多个 MySQL 实例,又没控制读写路径,很容易在写完主库后误读从库旧数据。最稳妥的方式不是靠 DB 层“等同步”,而是让应用自己决定该读谁。

典型做法是:对刚写入的记录,强制走主库查询(例如通过上下文标记、连接 hint 或注释);或者利用 GTID + WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS() 主动等待从库追上。

  • 写操作后需立即读同一行?加 /*+ USE_MASTER */ 注释(需代理或中间件支持)
  • 用 GTID 场景下,主库写完可调 SELECT WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS('xxx-yyy-zzz:123'); 等从库回放完成
  • 避免依赖 SELECT SLEEP(0.1) 这类拍脑袋延迟,不可靠且伤性能
  • 若用 MyCat / ShardingSphere 等中间件,务必开启 sqlHintmaster-slave-route 路由策略

监控和兜底:及时发现并定位不一致

复制不一致往往不会报错,而是静默发生。靠人工巡检几乎不可能,必须建立自动化检测机制。

核心手段是比对主从的 binlog positionGTID_EXECUTED 集合,并结合表级校验工具定期扫描。尤其要注意大事务、DDL、SET sql_log_bin=0 等绕过复制的操作。

  • 检查复制状态:SHOW SLAVE STATUSG 关注 Slave_IO_RunningSlave_SQL_RunningRetrieved_Gtid_SetExecuted_Gtid_Set 是否一致
  • pt-table-checksum 工具做表数据一致性校验(需主从都开启 binlog_row_image=FULL
  • 禁止在从库执行 STOP SLAVE; INSERT/UPDATE; START SLAVE; —— 这会导致 GTID 冲突或数据覆盖
  • 从库 read_only=ON 必须开启,但注意它不阻止 super 用户写入,必要时加 super_read_only=ON

实际中最容易被忽略的是:把“主从延迟低”等同于“数据一致”。即使 Seconds_Behind_Master = 0,也只代表 SQL Thread 当前没积压,不代表刚刚发生的事务已落盘或已提交。真正需要强一致的场景,得靠应用控制读写路径,而不是赌复制快。

text=ZqhQzanResources