MySQL查询执行流程分四步:解析→预处理→优化→执行;解析做词法语法分析,预处理校验语义(如表存在性),优化器生成执行计划,执行阶段调用存储引擎接口完成物理操作。

MySQL 查询执行流程分哪几步?
MySQL 执行一条 SELECT 语句,实际会经过「解析 → 预处理 → 优化 → 执行」四个核心阶段,中间不涉及存储引擎的物理读写,但每步都直接影响最终性能和结果正确性。
关键点在于:**解析和预处理由 Server 层完成,优化器决定执行计划,而执行阶段才真正调用存储引擎(如 InnoDB)接口**。这意味着即使 SQL 语法合法、字段存在,也可能在优化或执行时失败。
SQL 解析失败常见原因有哪些?
解析阶段主要做词法分析(Lexical Analysis)和语法分析(Syntax Analysis),不校验表或字段是否存在。
-
SELECT * FROM users WHERE age > ;—— 缺少右操作数,报错ERROR 1064 (42000) -
SELECT name, COUNT(*) FROM users;——name未出现在GROUP BY中且非聚合字段,在sql_mode=ONLY_FULL_GROUP_BY下解析通过但预处理失败 - 使用了 MySQL 8.0 新增的窗口函数(如
ROW_NUMBER())但在 5.7 环境下执行,会直接报ERROR 1305 (42000): FUNCTION xxx.ROW_NUMBER does not exist,这其实发生在解析后、函数注册检查时
为什么 EXPLAIN 显示的执行计划和实际执行不一致?
EXPLAIN 只模拟优化器生成的「预估计划」,它不触发真实执行,也不考虑运行时参数(如绑定变量值、临时内存限制、并发负载)。真实执行可能因以下原因绕过该计划:
- 优化器估算行数严重偏差(例如
WHERE created_at > '2020-01-01'实际匹配 95% 行,但统计信息陈旧,导致选了索引扫描而非全表扫描) -
JOIN顺序被运行时重排:当某张表返回结果集极小(如SELECT ... FROM config WHERE key = 'feature_x'),优化器可能动态调整驱动表 - 查询中含子查询或
UNION,EXPLAIN无法反映物化临时表行为;真实执行时可能生成DERIVED或MATERIALIZED表,触发磁盘临时表(Using temporary; Using filesort)
EXPLAIN FORMAT=TREE SELECT u.name FROM users u JOIN orders o ON u.id = o.user_id WHERE o.status = 'paid';
MySQL 8.0+ 的 FORMAT=TREE 比传统格式更贴近真实执行树,但仍不保证完全一致。
执行阶段哪些操作真正由存储引擎承担?
Server 层把优化后的执行计划翻译为「逐行请求」,交由存储引擎完成具体动作。InnoDB 主要响应以下调用:
-
index_read():按索引键定位记录(含主键/二级索引查找) -
row_search_for_mysql():执行范围扫描、ORDER BY排序(若索引能覆盖) -
ha_innobase::write_row()/update_row():DML 操作的具体实现 - 事务相关:
start_stmt()、commit()、rollback()最终映射为 InnoDB 的trx_start_if_not_started_xa()等内部事务控制
注意:WHERE 条件中的函数(如 YEAR(created_at) = 2023)无法下推到 InnoDB,Server 层必须从引擎拉回所有行再计算——这是索引失效的底层原因。
整个过程里最容易被忽略的是「预处理阶段的语义检查」:它比解析严格,但又比执行轻量。比如 SELECT COUNT(*) FROM non_existent_table 在预处理就报错,而 SELECT * FROM users WHERE id IN (SELECT user_id FROM nonexistent) 要到执行子查询时才暴露问题。










