这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 解析树经过语义解析和改写后,才能进入优化器。这个阶段会绑定表和列、展开视图、处理派生表、推导类型,并生成更适合优化的内部表示。
7.1 阶段目标
Parse Tree
-> name resolution
-> view / derived table merge
-> type aggregation
-> condition simplification
-> query block
-> optimizer
主要工作:
- 确认表、列、函数存在;
- 解析别名和作用域;
- 展开视图;
- 处理 CTE 和派生表;
- 推导表达式类型;
- 常量折叠;
- 条件简化;
- 权限检查准备;
- 构造
Query_block/Query_expression。
7.2 名称解析
示例:
SELECT u.id, o.no
FROM users u
JOIN orders o ON o.user_id=u.id
WHERE u.status='active';
解析内容:
users映射到表对象;u是表别名;u.id绑定字段;o.user_id参与连接条件;status的字符集和排序规则;- 权限上下文。
作用域错误示例:
SELECT id FROM users ORDER BY total
如果 total 不是输出列或可解析列,会在语义阶段报错。
7.3 视图展开
CREATE VIEW active_users AS
SELECT * FROM users WHERE status='active';
SELECT id FROM active_users WHERE id<100;
处理方式:
query on view
-> get view definition
-> merge or materialize
-> combined query block
简单视图可以合并到外层查询;包含聚合、LIMIT、UNION 等情况的视图可能物化。EXPLAIN 中会出现 DERIVED 或相关提示。
7.4 派生表与 CTE
WITH recent AS (
SELECT user_id, MAX(id) AS max_id
FROM orders GROUP BY user_id
)
SELECT * FROM recent LIMIT 10;
处理选择:
| 结构 | 可能方式 |
|---|---|
| 简单派生表 | merge |
| 聚合派生表 | materialize |
| 递归 CTE | 逐层执行 |
| 多次引用 CTE | 可能多次物化 |
| window + limit | 物化 |
optimizer_switch 和版本差异会影响合并行为,需用当前版本执行计划验证。
7.5 表达式与 Item
Server 层表达式常见为 Item 体系:
| 类型 | 示例 |
|---|---|
| 常量 | 1、'abc' |
| 字段 | Item_field |
| 函数 | Item_func_* |
| 聚合 | Item_sum_* |
| 子查询 | Item_subselect |
| 窗口 | window function item |
表达式类型推导影响:
- 比较;
- 隐式转换;
- 排序规则;
- 索引可用性;
- 临时表列类型;
- 结果元数据。
7.6 常量折叠
改写前:
SELECT * FROM t WHERE a = 1 + 1;
改写后:
a = 2
进一步优化:
- 布尔条件化简;
- 删除恒真条件;
- 识别不可能条件;
- 传播常量;
- 折叠简单函数;
- 规范化比较。
复杂函数是否可折叠取决于确定性和参数是否常量。
7.7 隐式转换
示例:
CREATE TABLE t(id INT PRIMARY KEY, vc VARCHAR(32), KEY idx_vc(vc));
SELECT * FROM t WHERE vc=123;
字符串列与数字比较可能导致规则和索引使用变化。语义阶段负责确定比较和转换方式,优化器据此判断索引条件。
排查:
EXPLAIN SELECT * FROM t WHERE vc='123';
EXPLAIN SELECT * FROM t WHERE vc=123;
生产建议尽量保持类型一致。
7.8 Query Block
优化器以 Query_block 组织 SELECT 语句单元:
Query_expression
|-- Query_block
| |-- tables
| |-- conditions
| |-- group by
| |-- having
| +-- order by
+-- set operations / union
子查询、CTE、视图都会形成一个或多个 query block。EXPLAIN FORMAT=TREE 能看到物化和连接顺序的近似结果。
7.9 调试与观测
断点:
b mysql_parse
b mysql_execute_command
b Query_block::prepare
b Query_block::resolve_placeholder_tables
观测:
SET optimizer_trace='enabled=on';
SELECT * FROM t WHERE a=1;
SELECT trace FROM information_schema.optimizer_trace\G
trace 中可看到 query block、条件、代价和候选计划信息。
7.10 常见问题
| 现象 | 阶段 |
|---|---|
| unknown column | name resolution |
| unknown table | open table / resolver |
| view merged | resolver |
| derived materialized | resolver / optimizer |
| implicit conversion | type derivation |
| condition removed | simplification |
| recursive CTE error | CTE semantic |
本章小结
解析后的重写阶段把语法结构绑定到真实表、列和类型,处理视图、派生表、CTE、常量折叠和条件简化。它是 Parser 与 Optimizer 之间承上启下的一层,许多“执行计划不符合书写直觉”的问题要从这里开始排查。
思考题
- 视图什么时候不能直接合并到外层查询?
Item表达式类型推导如何影响索引?- 派生表 merge 和 materialize 的区别是什么?
- 隐式转换为什么会改变执行计划?
- optimizer trace 中 query block 能提供哪些线索?