MySQL 8.0Notes

第 07 章:AST 与重写

zjc 于 2026-01-07 发布

这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 解析树经过语义解析和改写后,才能进入优化器。这个阶段会绑定表和列、展开视图、处理派生表、推导类型,并生成更适合优化的内部表示。

7.1 阶段目标

Parse Tree
  -> name resolution
  -> view / derived table merge
  -> type aggregation
  -> condition simplification
  -> query block
     -> optimizer

主要工作:

  1. 确认表、列、函数存在;
  2. 解析别名和作用域;
  3. 展开视图;
  4. 处理 CTE 和派生表;
  5. 推导表达式类型;
  6. 常量折叠;
  7. 条件简化;
  8. 权限检查准备;
  9. 构造 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';

解析内容:

  1. users 映射到表对象;
  2. u 是表别名;
  3. u.id 绑定字段;
  4. o.user_id 参与连接条件;
  5. status 的字符集和排序规则;
  6. 权限上下文。

作用域错误示例:

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

表达式类型推导影响:

  1. 比较;
  2. 隐式转换;
  3. 排序规则;
  4. 索引可用性;
  5. 临时表列类型;
  6. 结果元数据。

7.6 常量折叠

改写前:

SELECT * FROM t WHERE a = 1 + 1;

改写后:

a = 2

进一步优化:

  1. 布尔条件化简;
  2. 删除恒真条件;
  3. 识别不可能条件;
  4. 传播常量;
  5. 折叠简单函数;
  6. 规范化比较。

复杂函数是否可折叠取决于确定性和参数是否常量。

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 之间承上启下的一层,许多“执行计划不符合书写直觉”的问题要从这里开始排查。

思考题

  1. 视图什么时候不能直接合并到外层查询?
  2. Item 表达式类型推导如何影响索引?
  3. 派生表 merge 和 materialize 的区别是什么?
  4. 隐式转换为什么会改变执行计划?
  5. optimizer trace 中 query block 能提供哪些线索?