MySQL 8.0Notes

第 06 章:Parser

zjc 于 2026-01-06 发布

这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Parser 将 SQL 文本转换为解析树。它决定语法是否合法,并保留语句结构供语义解析和优化使用。

6.1 输入输出

SQL text
  -> lexer
     -> token stream
        -> parser
           -> Parse Tree
              -> Resolver / Transformer

Parser 只处理语法和基本结构,不做:

  1. 表是否存在;
  2. 列类型是否匹配;
  3. 权限是否允许;
  4. 索引是否有效;
  5. 执行代价是否合理。

这些属于后续阶段。

6.2 相关源码

文件 职责
sql/sql_lex.cc 词法与 LEX 相关逻辑
sql/sql_yacc.yy Bison 语法规则
sql/parser_yystype.h 语义值类型
sql/parse_tree_* 解析树节点
sql/sql_parse.cc 解析入口
sql/sql_parser.cc Parser 封装

入口调用:

mysql_parse
  -> parser_state initialization
     -> parse_sql
        -> generated parser
           -> Parse_tree_root

6.3 词法

词法分析处理:

  1. 关键字;
  2. 标识符;
  3. 字符串;
  4. 数字;
  5. 注释;
  6. 字符集前缀;
  7. 运算符;
  8. 参数占位符。

示例:

SELECT id, name FROM users WHERE id = ?

会被切分为关键字、标识符和符号 token。

字符集会影响字符串 token 和标识符解析,这也是某些 SQL 在不同 character_set_client 下表现不同的原因。

6.4 语法规则

Bison 规则示意:

select_statement:
  SELECT select_list
  FROM table_references
  WHERE expr

实际源码规则更复杂,包含:

  1. WITH CTE;
  2. window function;
  3. JOIN 语法;
  4. GROUP BY / HAVING;
  5. ORDER BY / LIMIT;
  6. 锁子句;
  7. UNION;
  8. 子查询;
  9. DML;
  10. DDL。

调试可从错误信息入手:

rg "syntax to use near" sql/

6.5 解析树节点

常见节点:

节点 对应语法
PT_select SELECT
PT_table_factor 表引用
PT_item_expr 表达式
PT_join JOIN
PT_window 窗口
PT_insert INSERT
PT_update UPDATE
PT_delete DELETE
PT_create_table CREATE TABLE

解析树保留源码书写结构,后续阶段会继续规范化和改写。

6.6 预处理语句

PREPARE s FROM 'SELECT * FROM t WHERE id=?';
SET @id=1;
EXECUTE s USING @id;
DEALLOCATE PREPARE s;

流程:

prepare
  -> parse
     -> prepare_expr / resolve
        -> keep statement structure

execute
  -> bind parameter
     -> optimize if needed
        -> execute

注意 max_prepared_stmt_count、会话生命周期和 SQL 注入防护。

6.7 错误处理

常见错误:

错误 阶段
ER_PARSE_ERROR 语法错误
ER_UNKNOWN_TABLE 解析后语义解析
ER_BAD_FIELD_ERROR 列解析
ER_SYNTAX_ERROR 语法规则失败
ER_NOT_SUPPORTED_YET 语法存在但特性不支持

解析错误要记录完整 SQL 和字符集环境。截断后的 SQL 可能无法复现。

6.8 调试 Parser

b mysql_parse
b parse_sql
run
bt
p parser_state
p thd->query().str

最小实验:

SELECT 1;
SELECT * FROM mysql.user LIMIT 1;
SELECT * FROM (SELECT 1) AS x;

观察不同语句生成的解析入口和树结构。

6.9 性能影响

解析通常不是最大瓶颈,但以下场景会放大:

  1. 超长 SQL;
  2. 大量 IN 列表;
  3. 深嵌套子查询;
  4. 频繁执行相同 SQL 但不使用 prepared statement;
  5. ORM 生成巨型查询;
  6. 每次都解析上千条批量值。

优化方向:

  1. 应用侧参数化;
  2. 拆分巨型 SQL;
  3. 减少不必要列;
  4. 批量写入分批;
  5. 缓存业务结果。

6.10 与优化器边界

Parser 输出的是“写了什么”,优化器决定“怎么做”。

SELECT * FROM t WHERE a=1;

Parser 只识别 SELECT、表和谓词;优化器再比较全表扫描、索引扫描和覆盖索引代价。

本章小结

Parser 负责把 SQL 文本转换为解析树,核心在词法、Bison 语法规则和 Parse Tree 节点。它不做权限和执行计划决策。读 Parser 时应从错误码、入口函数和最小语法用例切入。

思考题

  1. Parser 和 Resolver 的职责边界是什么?
  2. 字符集为什么会影响解析?
  3. prepared statement 在哪个阶段保存结构?
  4. 哪些 SQL 会放大解析成本?
  5. 如何通过 ER_PARSE_ERROR 定位源码?