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本身更有效。
