MySQL 查询解析器干了什么
解析器负责把
SELECT * FROM users WHERE id = 1这类文本 SQL 转成内部可识别的语法树(parse tree),不是直接执行,也不做权限或语义检查。
它只管三件事:词法分析(切分关键字、标识符、数字)、语法分析(验证是否符合 MySQL 语法规则)、生成初始语法树。
遇到ORDER BY写成
ORDERY BY,解析器立刻报错
ERROR 1064 (42000),不进下一步
SELECT a+b FROM t中的
a+b在这步只是被记为一个表达式节点,不计算值,也不查字段是否存在 解析器不关心表有没有索引、用户有没有权限——这些是后续模块的事
查询执行器真正干活的顺序
执行器拿到优化器输出的执行计划后,才开始调存储引擎接口取数据。它不解析 SQL,也不决定怎么查,只负责“按指令一步步跑”。
典型流程:
open table → find index or full scan → read rows → filter WHERE → sort if needed → send result即使
WHERE id = 1有索引,执行器仍要调用
ha_innobase::index_read()或类似接口,由 InnoDB 层完成实际定位 如果
WHERE条件里有函数,比如
WHERE YEAR(created_at) = 2023,执行器会在读出每行后调用函数计算,无法跳过无关行(除非函数能下推到引擎层) 执行器会反复调用
handler::rnd_next()或
handler::index_next(),直到返回
HA_ERR_END_OF_FILE
为什么有时 explain 显示用了索引,但慢查询日志里 still doing 'Sending data'
因为
Sending data状态在执行器阶段,表示正在从引擎读取并组装结果集——哪怕索引查找很快,但匹配行数巨大、或者需要回表、或者涉及临时表排序,都会卡在这步。
EXPLAIN的
type: ref只说明索引查找方式,不代表最终扫描/返回行数少 执行器对每一行都要做字段提取、类型转换、字符集处理,大字段(如
TEXT)或宽表会让这步变慢 如果查询含
GROUP BY且无法利用索引排序,执行器会建内部临时表+文件排序,
Creating sort index也会计入此状态
parser 和 executor 之间还有哪些关键中间环节
解析器输出语法树后,不会直接交给执行器。中间至少经过:预处理器(检查表/列是否存在)、优化器(重写查询、选择索引、生成执行计划)。
预处理器发现SELECT * FROM nonexistent_table,报错
ERROR 1146 (42S02),比解析器晚、比优化器早 优化器可能把
SELECT a FROM t WHERE a > 1 AND a 改写成 <code>SELECT a FROM t WHERE a BETWEEN 2 AND 9,执行器看到的是改写后的计划 执行器拿到的是
JOIN_TAB数组和访问方法(
JT_ALL,
JT_INDEX_SCAN等),不是原始 SQL
真正容易被忽略的是:解析器和执行器之间没有缓存,每次查询都重走全流程;而查询缓存(已弃用)或 Plan Cache(8.0.22+ 的
query_cache_type=OFF下不可用)影响的是更上层,跟这两个模块本身无关。
