mysql执行select语句需经词法语法分析、查询重写、逻辑优化、物理优化、执行五个阶段;各阶段异常会导致慢查或报错,需结合slow_log、show profile及统计信息更新等定位根因。

MySQL执行SQL查询时,到底发生了什么
一条 SELECT 语句发给MySQL,并不等于立刻去磁盘读数据。它会先走一套完整的解析、优化、执行链路,中间任何一环出问题都可能导致慢、错、甚至拒绝执行。
SQL从输入到结果的五个关键阶段
MySQL服务端收到查询后,按顺序经历以下阶段(可通过 EXPLAIN FORMAT=TREE 或开启 optimizer_trace 观察):
-
词法与语法分析:检查
SELECT * FROM t WHERE id = ?是否符合MySQL语法规则;非法字段名、缺失逗号、错误关键字都会在这里报错,如ERROR 1054 (42S22): Unknown column 'xxx' in 'field list' -
查询重写:将视图展开、去除无用的
ORDER BY(如子查询中)、合并常量条件(WHERE 1=1 AND a=5→WHERE a=5) -
逻辑优化(Logical Optimization):基于规则改写,比如谓词下推(把
WHERE条件尽可能下推到 JOIN 或子查询内部)、等价变换(a > 5 AND a → <code>a BETWEEN 6 AND 9) -
物理优化(Cost-based Optimization):生成多个执行计划候选(如用
idx_a还是idx_b,是否走索引合并),估算每个计划的IO/内存/CPU开销,选成本最低的——这就是为什么EXPLAIN显示的key不一定和你预期一致 -
执行引擎调用:调用存储引擎接口(如InnoDB的
ha_innobase::index_read()或ha_innobase::rnd_next()),真正读取数据页、加锁、返回记录
为什么EXPLAIN显示走了索引,但实际还是慢
常见误解是“有 key 就快”,其实不然。几个关键干扰点:
- 索引扫描行数(
rows)远大于实际返回行数(filtered值低),说明大量回表或无效过滤,比如WHERE status IN ('A','B','C') AND create_time > '2023-01-01',但只有status有索引,create_time条件被当成 post-filter 处理 - 使用了索引但触发了
Using filesort或Using temporary,意味着排序或分组没走索引,而是在内存或磁盘临时表里做 - 统计信息过期(
ANALYZE TABLE没跑过),优化器误判数据分布,选了全索引扫描而非范围扫描 - 隐式类型转换导致索引失效,例如字段是
VARCHAR,但查询写了WHERE phone = 13800138000(数字字面量),MySQL自动转成CAST(phone AS DOUBLE),索引无法使用
如何快速定位执行卡在哪一步
别只看 EXPLAIN,结合运行时指标交叉验证:
- 开启
slow_query_log并设置long_query_time = 0,配合log_output = 'TABLE',查mysql.slow_log看真实耗时分布(Lock_time,Rows_sent,Rows_examined) - 对单条查询加
/*+ MAX_EXECUTION_TIME(1000) */防止长尾拖垮连接池 - 用
SHOW PROFILE FOR QUERY N(需先SET profiling = 1)查看各阶段耗时,重点关注statistics(优化器耗时)、executing(执行本身)、Sending data(构造结果集,常被误认为IO) - InnoDB层可查
INFORMATION_SCHEMA.INNODB_TRX和INNODB_LOCK_WAITS,确认是否被锁阻塞而非真慢
执行流程不是黑盒,但每个环节的偏差都可能被放大。最常被跳过的其实是更新统计信息和检查隐式转换——这两步花30秒,往往比调优SQL本身更有效。










