这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 MySQL 8.0 使用事务性数据字典存储表、列、索引、字符集和约束等元数据。Server 层表缓存、MDL 和 InnoDB 字典缓存共同决定 DDL 与 DML 的并发行为。
10.1 元数据演进
| 版本 | 方式 |
|---|---|
| 5.7 及以前 | .frm 等文件保存表定义 |
| 8.0 | DD 表存储元数据 |
8.0 带来:
- 原子 DDL;
- 元数据事务化;
- 支持更完整的字符集和 collation;
- 移除
.frm; - 统一系统表存储。
原子 DDL 表示 DDL 的数据字典更新整体提交或回滚,不等价于所有 DDL 都不锁表或不重建表。
10.2 核心对象
| 对象 | 职责 |
|---|---|
| DD tables | 存储元数据 |
| Dictionary Client | Server 层访问接口 |
dd::Table |
表定义 |
dd::Column |
列定义 |
dd::Index |
索引定义 |
TABLE_SHARE |
可共享表定义 |
TABLE |
每个表实例的运行时对象 |
| InnoDB dict cache | 引擎层缓存 |
10.3 表缓存
open_tables
-> table cache hit?
|-- yes: reuse TABLE
+-- no: load DD definition
-> create TABLE
-> open handler
相关参数:
| 参数 | 说明 |
|---|---|
table_open_cache |
表缓存数量 |
table_open_cache_instances |
缓存分区 |
table_definition_cache |
表定义缓存 |
open_files_limit |
文件句柄上限 |
观测:
SHOW GLOBAL STATUS LIKE 'Open_%';
SHOW GLOBAL STATUS LIKE 'Opened_%';
10.4 MDL
元数据锁保护表结构在语句或事务期间不被不兼容 DDL 破坏。
常见锁:
| 类型 | 场景 |
|---|---|
| SHARED_READ | SELECT |
| SHARED_WRITE | DML |
| SHARED_UPGRADABLE | 部分 DDL 前置阶段 |
| EXCLUSIVE | 阻塞读写 |
问题场景:
长事务持有 SHARED_READ
-> DDL 等待 EXCLUSIVE
-> 后续 DML 排队
排查:
SELECT * FROM performance_schema.metadata_locks;
SELECT * FROM sys.schema_table_lock_waits;
10.5 DDL 阶段
通用阶段:
acquire MDL
-> prepare
-> algorithm decision
-> prepare output table
-> alter table data if needed
-> commit DD
-> release locks
ALGORITHM:
| 算法 | 说明 |
|---|---|
| INSTANT | 只改元数据,受版本和操作限制 |
| INPLACE | 引擎内执行,可能允许并发 DML |
| COPY | 重建表并复制数据 |
指定不支持的算法会报错:
ALTER TABLE t ADD COLUMN c INT, ALGORITHM=INSTANT;
10.6 LOCK 策略
ALTER TABLE t ADD INDEX idx_c(c), ALGORITHM=INPLACE, LOCK=NONE;
| 锁 | 含义 |
|---|---|
| NONE | 允许并发 DML |
| SHARED | 允许读,阻塞写 |
| EXCLUSIVE | 阻塞读写 |
实际支持由操作、索引类型、版本和引擎决定:
SHOW ALGORITHM STATUS;
或查看 information_schema.innodb_alter_algorithm,取决于版本支持情况。
10.7 临时表
SQL 层临时表:
- 会话可见;
- 不进入 binlog 主流复制路径;
- 可由引擎内部临时表或用户 TEMPORARY 表;
- DDL 和 MDL 行为不同。
内部临时表可能由内存引擎转为磁盘 InnoDB 临时表,受 internal_tmp_mem_storage_engine、tmp_table_size、max_heap_table_size 等影响。
10.8 字符集与排序规则
元数据记录:
- 表默认字符集;
- 列字符集;
- collation;
- 表达式推导规则。
示例:
CREATE TABLE t(
a VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
b VARCHAR(32)
) DEFAULT CHARSET=utf8mb4;
collation 影响:
- 等值比较;
- 排序;
- 索引查找;
- 前缀匹配;
- 函数结果推导。
10.9 DDL 故障排查
| 现象 | 方向 |
|---|---|
| DDL 等待 metadata lock | 长事务、表缓存 |
| DDL 后查询计划变化 | 统计、索引、字符集 |
| instant 失败 | 操作或版本不支持 |
| inplace 长时间 | 在线 DDL 日志和重建 |
| copy 占用磁盘 | 临时表空间 |
| DDL 卡在 finalize | 锁和后台操作 |
处理:
- 找 waiting thread;
- 找 blocking thread;
- 评估长事务;
- 使用低峰窗口;
- 预留磁盘;
- 大表使用 gh-ost/pt-osc 评估;
- 记录回滚方案。
本章小结
表与元数据链路覆盖 DD 存储、表定义缓存、运行时 TABLE、handler 打开和 MDL。理解这条链路能解释 DDL 等待、原子性、并发 DML 和统计刷新行为,也是后续读 DDL 源码的基础。
思考题
- MySQL 8.0 原子 DDL 解决了什么问题?
TABLE_SHARE和TABLE的区别是什么?- 为什么长事务会阻塞 DDL 并间接阻塞后续 DML?
- INSTANT、INPLACE、COPY 的核心差异是什么?
- collation 如何影响索引查找?