主从复制中SQL执行流程本质未变,但执行主体变为重放binlog事件:ROW模式直接行应用,跳过解析/优化;STATEMENT模式才执行原始SQL,存在非确定性风险。

主从复制中 SQL 执行流程本质没变,但执行主体和上下文完全不同
MySQL 主库上执行 INSERT、UPDATE 等语句时,会走完整的解析 → 优化 → 执行 → 写 binlog 流程;而从库不是“重新执行原始 SQL”,而是**重放主库生成的 binlog 事件**。这意味着:从库不走 SQL 解析器(parser),不触发查询重写(query rewrite),也不经过查询缓存(已弃用)或权限校验环节。
从库执行的是 Relay Log 中的 Events,不是原始 SQL 字符串
主库 binlog 记录的是逻辑变更事件(如 Write_rows_event、Update_rows_event)或语句事件(Query_event),取决于 binlog_format 设置。从库的 SQL Thread 读取 relay log 后:
- 若为
ROW格式:直接按行应用变更,跳过所有 SQL 层逻辑,不依赖表结构一致性(但要求列数/类型兼容) - 若为
STATEMENT格式:才真正“执行”文本 SQL,此时会走完整 SQL 流程 —— 但风险高(如含NOW()、UUID()、用户变量等非确定性函数) -
MIXED格式由 MySQL 自动切换,对用户透明,但难以预测哪条走哪种路径
常见问题都源于“执行上下文错位”,不是流程本身出错
以下现象常被误认为“SQL 执行流程变了”,实际是主从环境差异导致:
-
Duplicate entry错误:从库已有数据,但主库 binlog 里没带INSERT IGNORE或ON DUPLICATE KEY UPDATE -
Table doesn't exist:主库删表后又建同名表,但从库 relay log 应用顺序被打断(比如中途 stop slave + reset slave) - 时间函数结果不一致:
STATEMENT模式下SYS_DATE()在从库执行时取的是从库系统时间 - 自增 ID 跳变:主库批量插入触发 auto_increment 预分配,但 binlog 只记录最终值,从库无法还原中间状态
验证执行路径最直接的方式是看 SHOW PROCESSLIST 和日志
在从库执行:
SHOW PROCESSLIST;
你会看到两个关键线程:
-
IO Thread:负责拉取主库 binlog,写入本地 relay log -
SQL Thread:负责读 relay log 并应用 —— 它的状态显示为Reading event from the relay log或Executing event,而非executing原始 SQL
开启 log_slave_updates 后,再查从库的 SHOW BINLOG EVENTS,能看到 relay log 被转写成本地 binlog 的过程,进一步确认它不是“二次解析原始 SQL”,而是基于事件的转换。
真正容易被忽略的是:即使你用的是 ROW 格式,如果从库表结构缺失索引、或没有主键,UPDATE/DELETE 可能退化为全表扫描匹配 —— 这不是执行流程变了,是底层引擎在无索引约束下的自然行为。
