MySQL 8.0Notes

第 03 章:源码阅读方法

zjc 于 2026-01-03 发布

这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 MySQL 源码量大,直接逐行读效率很低。有效方法是问题驱动:从现象入手,建立调用链,再用调试器和测试验证。

3.1 问题驱动

阅读前先定义问题:

现象:SELECT 使用二级索引后为什么回表?
问题:InnoDB 在哪里从二级索引回到聚簇索引?
验证:断点 row_search_mvcc
输出:调用链和最小复现

适合入手的问题:

  1. 一条 SQL 的解析和执行路径;
  2. 一个执行计划为什么选择某个索引;
  3. 一个锁为什么被持有;
  4. 一个参数如何生效;
  5. 一个状态计数在哪里增加;
  6. 一个错误码在哪里返回。

3.2 从入口到出口

SELECT 调用链:

dispatch_command
  -> mysql_parse
     -> mysql_execute_command
        -> open_tables
        -> optimize
        -> execute
           -> handler::ha_rnd_next / ha_index_read
              -> InnoDB row operations

UPDATE 调用链:

parse
  -> resolve
     -> optimize
        -> scan/index read
           -> row update
              -> undo log
              -> redo log
              -> binlog
              -> commit

先画粗链路,再深入分支。

3.3 静态搜索

常用搜索:

rg "dispatch_command" sql/
rg "class THD" sql/ include/
rg "row_search_mvcc" storage/innobase/
rg "SAVEPOINT" sql/ storage/innobase/
rg "ER_DUP_ENTRY" sql/ include/

搜索策略:

  1. 函数名;
  2. 错误码字符串;
  3. 状态变量名;
  4. 配置参数名;
  5. 注释中的 RFC 或 Worklog;
  6. MTR 测试名;
  7. 结构体字段。

3.4 调用栈验证

静态搜索会给出多个候选,调用栈能确认实际路径。

b row_search_mvcc
run
bt
#0 row_search_mvcc
#1 row_search_index_step
#2 row_search_for_mysql
#3 ha_innobase::index_read

验证内容:

  1. 入口函数;
  2. 参数含义;
  3. 分支条件;
  4. 返回值;
  5. 错误路径;
  6. 资源释放;
  7. 调用方。

3.5 阅读结构体

不要一开始读完所有字段,先看生命周期:

结构 生命周期 关注点
THD 一个客户端线程 security、query、transaction
TABLE 打开的表对象 字段、索引、触发器
handler 一次表访问 引擎接口
row_prebuilt_t InnoDB 表操作上下文 游标、事务、模板
trx_t InnoDB 事务 id、state、lock
mtr_t mini transaction 页修改和日志

字段含义可通过初始化、赋值和引用位置交叉验证。

3.6 版本对比

git log --oneline -- sql/sql_optimizer.cc
git blame -L 100,160 sql/sql_executor.cc
git show <commit> -- storage/innobase/lock/lock0lock.cc

版本对比适合:

  1. 参数默认值变化;
  2. 新执行计划输出;
  3. 锁实现重构;
  4. 数据字典改造;
  5. 性能优化引入的行为变化。

结论必须落到当前使用版本。

3.7 结合观测工具

工具 作用
EXPLAIN FORMAT=TREE 执行计划
optimizer_trace 代价和候选计划
performance_schema 等待和阶段
SHOW ENGINE INNODB STATUS 事务和锁
information_schema.innodb_trx 当前事务
sys.innodb_lock_waits 锁等待
error log 启动和异常

示例:

SET optimizer_trace="enabled=on";
SELECT * FROM t WHERE a=1;
SELECT trace FROM information_schema.optimizer_trace;

3.8 记录源码笔记

建议模板:

版本:mysql-8.0.40
问题:
复现 SQL:
调用链:
关键函数:
关键结构:
分支条件:
验证方式:
结论:
注意事项:

笔记要保存复现命令,不只是函数名。

3.9 常见误区

误区 后果
逐行读所有代码 快速失去全局
只看博客结论 版本不匹配
只看函数名 忽略状态和锁
不看调用方 误解生命周期
不写复现 无法验证
混淆 5.7 与 8.0 结论过时
忽略测试用例 遗漏边界行为

3.10 阅读路线

推荐顺序:

连接与线程
  -> Parser / Resolver
     -> Optimizer
        -> Executor
           -> handler
              -> InnoDB page / B+Tree
                 -> Buffer Pool
                    -> trx / lock / undo
                       -> redo / recovery
                          -> background threads

先掌握数据路径,再深入恢复和复制。

本章小结

源码阅读要以现象为入口、调用链为主线、调试器和 MTR 为验证手段。搜索、断点、版本对比和观测工具结合,才能把源码结论变成可复现、可落地的内核理解。

思考题

  1. 为什么静态搜索不能替代调用栈?
  2. 阅读 THD 时应先关注哪些生命周期信息?
  3. git blame 在源码研究中的用途是什么?
  4. optimizer trace 能帮助定位哪类源码问题?
  5. 如何写一份可复现的源码笔记?