这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 MySQL 8.0 源码庞大,但它有清晰的模块边界。读源码的第一步不是逐行阅读,而是建立地图:连接如何进入 Server 层,SQL 如何解析和优化,执行器如何调用存储引擎,InnoDB 如何管理页面、事务、锁和日志。
本章目标是建立 MySQL 8.0 源码阅读的全局视角。
1.1 为什么要读源码
读源码能解决文档没有覆盖的问题:
- 理解执行计划为什么这样选;
- 理解锁范围为什么比预期大;
- 理解参数默认值背后的机制;
- 理解错误日志和状态指标来源;
- 判断性能问题在 SQL 层还是引擎层;
- 验证社区博客结论是否适用当前版本;
- 为内核修改和深度排障打基础。
读源码不是为了背代码行数,而是为了建立可验证的机制模型。
1.2 源码目录概览
以 MySQL 8.0 社区源码为例,常见目录:
| 目录 | 职责 |
|---|---|
sql/ |
SQL 层核心、解析、优化、执行、协议 |
storage/innobase/ |
InnoDB 存储引擎 |
include/ |
公共头文件 |
mysys/ |
系统库封装 |
libbinlogevents/ |
Binlog 事件定义 |
client/ |
客户端工具 |
router/ |
MySQL Router |
plugin/ |
插件 |
unittest/ |
单元测试 |
mysql-test/ |
MTR 测试 |
cmake/ |
构建配置 |
最重要的两个目录:
sql/
-> Server layer
storage/innobase/
-> InnoDB
1.3 一条 SQL 的调用链
查询的大致路径:
mysql_real_query / COM_QUERY
-> dispatch_command
-> mysql_parse
-> parse_tree
-> mysql_execute_command
-> open_tables
-> optimize
-> execute
-> handler interface
-> ha_innobase
-> row_search / row_insert / row_update
写入语句还涉及:
Server层
-> binlog prepare
-> engine prepare
-> binlog write
-> engine commit
这就是 redo log 和 binlog 的两阶段提交路径。
1.4 SQL 层核心模块
| 模块 | 主要职责 |
|---|---|
| Connection Handler | 管理客户端连接和线程 |
| Protocol | 编解码 MySQL 协议包 |
| Parser | 语法解析 |
| Resolver | 语义解析、表和列解析 |
| Transformer | 逻辑改写 |
| Optimizer | 代价估算、连接顺序、访问方法 |
| Executor | 执行迭代器 |
| Table Cache | 表定义缓存 |
| Lock Manager | Server 层元数据锁 |
| Binlog | 逻辑日志 |
MySQL 8.0 优化器和执行器相比 5.6/5.7 有明显重构,例如基于迭代器的执行模型和新优化器结构。
1.5 InnoDB 核心模块
| 模块 | 职责 |
|---|---|
| fil | 文件空间管理 |
| buf | Buffer Pool |
| btr | B+Tree |
| row | 行操作 |
| rem | 记录管理 |
| lock | 锁系统 |
| trx | 事务 |
| undo | 回滚段和版本链 |
| log | redo log |
| mtr | mini transaction |
| page | 页面管理 |
| ibuf | Change Buffer |
| purge | 清理 delete mark |
| dict | 数据字典缓存 |
| fts | 全文索引 |
推荐阅读顺序:
buf -> page -> btr -> row -> trx -> lock -> undo -> log -> purge
先懂页面和索引,再懂事务和锁,最后读恢复和后台线程。
1.6 编译与调试准备
获取源码:
git clone https://github.com/mysql/mysql-server.git
cd mysql-server
git checkout mysql-8.0.xx
常见依赖:
sudo apt install build-essential cmake ninja-build \
libssl-dev libncurses-dev libtirpc-dev \
bison pkg-config
构建:
mkdir build
cd build
cmake .. -GNinja \
-DCMAKE_BUILD_TYPE=Debug \
-DWITH_UNIT_TESTS=OFF \
-DWITH_DEBUG=ON
ninja
启动调试:
./runtime_output_directory/mysqld --defaults-file=my.cnf --user=mysql
调试建议:
- 使用 Debug 构建;
- 固定小数据集;
- 用 MTR 复现;
- 在 SQL 层入口和 handler 接口断点;
- 结合
EXPLAIN FORMAT=TREE; - 结合 performance_schema;
- 阅读对应测试用例。
1.7 源码阅读方法
推荐方法:
- 先写可复现实验;
- 再看执行计划;
- 根据现象猜测模块;
- 从入口函数进入;
- 画调用链;
- 记录关键结构体;
- 用断点验证;
- 对照测试;
- 输出笔记。
例如调查二级索引回表:
CREATE TABLE t (
id BIGINT PRIMARY KEY,
a INT,
b VARCHAR(64),
KEY idx_a(a)
) ENGINE=InnoDB;
EXPLAIN SELECT * FROM t WHERE a = 10;
源码路径:
Optimizer 选择 ref/index scan
-> Executor 调用 handler read
-> InnoDB secondary index lookup
-> row_search_mvcc
-> return PK
-> clustered index lookup
1.8 关键数据结构
| 结构 | 说明 |
|---|---|
THD |
一个客户端线程的会话上下文 |
TABLE |
打开的表对象 |
handler |
Server 调用引擎的接口 |
row_prebuilt_t |
InnoDB 表操作上下文 |
buf_block_t |
Buffer Pool 页块 |
rec_t |
InnoDB 记录 |
btr_cur_t |
B+Tree 游标 |
trx_t |
InnoDB 事务 |
lock_t |
InnoDB 锁对象 |
mtr_t |
mini transaction |
不要一开始就追所有字段,先理解生命周期和模块关系。
本章小结
MySQL 8.0 源码可以按 Server 层和 InnoDB 分开阅读。Server 层关注连接、解析、优化、执行和 Binlog;InnoDB 关注页面、B+Tree、事务、锁、MVCC、日志和后台线程。源码阅读要从可复现实验和调用链入手,用调试器和测试验证理解。
思考题
sql/和storage/innobase/的边界是什么?- 一条 SELECT 的关键调用链包含哪些阶段?
- redo log 和 binlog 两阶段提交涉及哪些模块?
- 为什么建议先读 Buffer Pool、Page、B+Tree,再读事务和锁?
- 如何用 MTR 测试验证一个源码结论?