03_MySQL 存储引擎与存储管理 ------ InnoDB 深入、表空间/ibd、binlog/redo/undo 与双写缓冲
系列定位 :《MySQL 数据库全栈运维实战系列》第 03 篇,与《Oracle 数据库全栈运维实战系列(VMware 版)》同构对标。适用版本 :MySQL 8.0(5.7 差异随文标注)。官方文档:MySQL 8.0 Reference Manual、InnoDB Storage Engine / InnoDB On-Disk Structures / The Binary Log。
一、存储引擎全景(可插拔架构)

图 1
| 特性 | InnoDB | MyISAM | MEMORY | 类比 Oracle |
|---|---|---|---|---|
| 事务 ACID | ✅ | ❌ | ❌ | 对应 OLTP 表 |
| 锁粒度 | 行锁(默认) | 表锁 | 表锁 | 行锁 vs 表锁 |
| 外键 | ✅ | ❌ | ❌ | 约束 |
| 崩溃恢复 | ✅(redo 重放) | ❌ | ❌(重启即失) | instance recovery |
| MVCC | ✅ | ❌ | ❌ | undo 多版本 |
| 全文索引 | 8.0 支持 | ✅ | ❌ | 不适用 |
| 压缩 | ✅( transparent ) | ✅(myisampack) | - | 表压缩 |
| 存储位置 | 磁盘(.ibd) | 磁盘(.MYD/.MYI) | 内存 | 数据文件 |
| 生产定位 | 默认唯一选择 | 只读历史报表 | 临时缓存/汇总 | - |
核心结论 :MySQL 8.0 生产环境 100% 使用 InnoDB。MyISAM 仅在明确的只读归档场景偶见;MEMORY 用于临时表/会话级缓存。
二、InnoDB 物理存储结构(官方:InnoDB On-Disk Structures)
2.1 逻辑层级

图 2
| 层级 | 大小/说明 | 类比 Oracle |
|---|---|---|
| 表空间 Tablespace | 一个或多个数据文件 | 表空间 |
| 段 Segment | 数据段/索引段/回滚段 | 段 |
| 区 Extent | 1MB = 64 × 16KB 页 | 区 (extent) |
| 页 Page | 16KB(默认,可改 4K/8K/16K/32K/64K) | 数据块 block |
| 行 Row | 记录存储在页中 | 行 |
2.2 表空间类型
| 表空间类型 | 说明 | 对应文件 | 类比 Oracle |
|---|---|---|---|
| 系统表空间 | 数据字典、change buffer、回滚段 | ibdata1 | SYSTEM 表空间 |
| 独立表空间 | 每表一个 .ibd(8.0 默认 innodb_file_per_table=ON) | 库名/表名.ibd | 本地管理表空间 |
| 通用表空间 | CREATE TABLESPACE 手动创建 | 自定义路径 | 大文件表空间 |
| undo 表空间 | 独立 undo 日志(8.0 可在线调整) | undo_001, undo_002 | UNDO 表空间 |
| 临时表空间 | 临时表存储 | ibtmp1 | TEMP 表空间 |
-- 解释:查看所有表空间
SELECT TABLESPACE_NAME, FILE_NAME, ENGINE FROM information_schema.FILES;
-- 解释:查看某表使用的表空间
SELECT TABLE_NAME, TABLESPACE_NAME FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'appdb' AND TABLE_NAME = 't_order';
-- 解释:查看 .ibd 文件大小(每表独立表空间)
SHOW TABLE STATUS FROM appdb LIKE 't_order'\G
8.0 变化 :5.7 之前默认
innodb_file_per_table=OFF,所有表共用 ibdata1,导致 ibdata1 膨胀无法回收;8.0 默认 ON,每表一个 .ibd,DROP/TRUNCATE 表后空间可回收给操作系统。
三、三大日志:redo log / undo log / binlog
3.1 三大日志职责对比

图 3
| 日志 | 级别 | 作用 | 写入者 | 类比 Oracle |
|---|---|---|---|---|
| redo log | InnoDB 引擎层 | 崩溃恢复(前滚),保证持久性 | InnoDB | redo 日志 |
| undo log | InnoDB 引擎层 | 事务回滚 + MVCC 版本链 | InnoDB | undo 段 |
| binlog | Server 层 | 主从复制 + 时间点恢复(PITR) | MySQL Server | 归档日志 |
关键区别 :redo 和 undo 是 InnoDB 引擎层 日志,binlog 是 MySQL Server 层日志。这就是为什么 MyISAM 没有崩溃恢复------它没有 redo log。
3.2 redo log(重做日志)
-- 解释:查看 redo log 配置(8.0.30+ 使用 innodb_redo_log_capacity)
SHOW VARIABLES LIKE 'innodb_redo_log_capacity'; -- 总容量,默认 100M
SHOW VARIABLES LIKE 'innodb_log_buffer_size'; -- redo 缓冲,默认 16M
-- 解释:查看 redo 日志文件(8.0 在线 redo 目录)
SHOW VARIABLES LIKE 'innodb_log_group_home_dir'; -- 默认 ./ 即 datadir
| 参数 | 说明 | 推荐值 |
|---|---|---|
| innodb_redo_log_capacity | redo 总容量(8.0.30+) | 2G~4G(高写入场景) |
| innodb_log_buffer_size | redo 内存缓冲 | 64M(大事务场景) |
| innodb_flush_log_at_trx_commit | 刷盘策略 | 1(最安全) |
| innodb_log_writes_advances | 检查点推进频率 | 默认即可 |
5.7 差异 :5.7 使用
innodb_log_file_size+innodb_log_files_in_group两个参数管理 redo;8.0.30+ 统一为innodb_redo_log_capacity。
3.3 undo log(回滚日志)
-- 解释:查看 undo 表空间
SHOW VARIABLES LIKE 'innodb_undo_directory'; -- undo 目录
SHOW VARIABLES LIKE 'innodb_undo_tablespaces'; -- undo 表空间数量
-- 解释:查看当前 undo 段使用情况
SELECT * FROM information_schema.INNODB_UNDO_LOGS;
| 作用 | 说明 |
|---|---|
| 事务回滚 | ROLLBACK 时用 undo 恢复旧值 |
| MVCC 一致性读 | 快照读通过 undo 版本链找到历史版本 |
| purge 线程 | 清理已提交事务不再需要的 undo 版本 |
3.4 binlog(二进制日志)
-- 解释:查看 binlog 配置
SHOW VARIABLES LIKE 'log_bin'; -- ON 表示已开启
SHOW VARIABLES LIKE 'binlog_format'; -- ROW / STATEMENT / MIXED
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds'; -- 保留时长(秒)
SHOW MASTER STATUS; -- 当前 binlog 位点
| binlog 格式 | 记录内容 | 优点 | 缺点 | 适用 |
|---|---|---|---|---|
| ROW(8.0 默认) | 每行数据的前后镜像 | 数据安全、准确 | 日志体积大 | 生产默认 |
| STATEMENT | 原始 SQL 语句 | 日志小 | 函数不确定(NOW()/UUID())可能主从不一致 | 批量大事务 |
| MIXED | 自动选择 | 折中 | 复杂度高 | 兼容场景 |
四、Doublewrite Buffer(双写缓冲)
4.1 为什么需要双写

图 4
| 问题 | 说明 | 类比 Oracle |
|---|---|---|
| 页半写(torn page) | 16KB 页只写了一部分就断电 | 块断裂 |
| 页大小与 OS 块不对齐 | MySQL 页 16KB,OS 块 4KB,中间断电导致部分写入 | - |
| 双写作用 | 先写一份到共享表空间的双写区,再写真正位置;崩溃时从双写区恢复 | 类似 Oracle 的块恢复 |
-- 解释:查看双写状态
SHOW VARIABLES LIKE 'innodb_doublewrite'; -- ON(默认开启)
SHOW STATUS LIKE 'innodb_dblwr%'; -- 双写统计
性能影响 :双写会带来约 5~10% 的写入性能开销,但换取了崩溃安全性,生产环境必须开启。
五、Change Buffer(变更缓冲)

图 5
| 参数 | 说明 |
|---|---|
| innodb_change_buffering | 缓冲类型:all/none/inserts/deletes/changes/pures |
| innodb_change_buffer_max_size | Change Buffer 最大占 Buffer Pool 百分比,默认 25% |
原理:二级索引页不在内存时,先把变更记录到 Change Buffer,避免随机读盘,等后续访问该页时再合并回真实数据。显著提升写多读少场景的性能。
六、Buffer Pool 深入(官方:InnoDB Buffer Pool)

图 6
| 组件 | 作用 | 关键参数 |
|---|---|---|
| LRU 链表 | 管理所有缓存页,young/old 分区防全表扫描污染 | innodb_old_blocks_pct(默认 37%) |
| Free 链表 | 空闲可用页 | - |
| Flush 链表 | 脏页列表,后台线程异步刷盘 | innodb_max_dirty_pages_pct(默认 90) |
| Change Buffer | 二级索引写缓冲 | innodb_change_buffering |
-- 解释:Buffer Pool 命中率监控
SHOW ENGINE INNODB STATUS\G
-- 重点看 BUFFER POOL AND MEMORY 段:
-- Buffer pool hit rate: 999/1000(越高越好,< 990 需关注)
七、表空间管理实操
7.1 查看表空间
-- 解释:所有表空间概览
SELECT TABLESPACE_NAME, FILE_NAME, EXTENT_SIZE, INITIAL_SIZE/1024/1024 AS initial_mb
FROM information_schema.FILES WHERE FILE_TYPE='DATAFILE';
-- 解释:查看独立表空间文件大小
SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE, ROUND(DATA_LENGTH/1024/1024,2) AS data_mb,
ROUND(INDEX_LENGTH/1024/1024,2) AS index_mb
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','sys','information_schema','performance_schema');
7.2 收缩表空间(OPTIMIZE TABLE)
-- 解释:删除大量数据后 .ibd 文件不会自动缩小给 OS,需 OPTIMIZE 回收空间
-- 原理:重建表 + 索引,相当于 MySQL 版的 MOVE/SPLICE
OPTIMIZE TABLE appdb.t_order;
-- 5.7 注意:OPTIMIZE 在 InnoDB 上实际是 CREATE + INSERT + RENAME,会锁表
-- 8.0 Online DDL 支持在线 OPTIMIZE(短暂元数据锁)
7.3 表空间迁移(Transportable Tablespace)
-- 解释:导出表空间(类似 Oracle 的 transportable tablespace)
FLUSH TABLES appdb.t_order FOR EXPORT; -- 加读锁,生成 .cfg
-- 此时复制 t_order.ibd 和 t_order.cfg 到目标机
UNLOCK TABLES;
-- 在目标机导入
ALTER TABLE appdb.t_order DISCARD TABLESPACE;
-- 复制 .ibd 和 .cfg 到目标数据目录
ALTER TABLE appdb.t_order IMPORT TABLESPACE;
八、VMware 虚机存储注意事项
| 项 | 建议 | 原因 |
|---|---|---|
| 数据盘独立 | /data 单独挂载一块虚拟磁盘,与系统盘分离 | 便于扩容、快照独立、IO 隔离 |
| 磁盘格式 | Thick Eager Zeroed | 数据库 IO 延迟敏感,Thin 首次写入有分配延迟 |
| IO 控制器 | Paravirtual (PVSCSI) | 比 LSI Logic SAS 更高 IOPS,适合数据库 |
| 磁盘缓存 | 禁用读写缓存(虚拟机设置 → 高级) | InnoDB 已有自己的 Buffer Pool,OS/VMware 缓存多余 |
| 磁盘模式 | Independent - Persistent | 防止快照回滚导致数据文件与 binlog 不一致 |
| SSD 直通 | 宿主机 SSD 可直通给虚机(Virtual Disk 选 SSD) | 显著提升随机 IO 性能 |
九、踩坑记录(实测汇总)
| # | 坑 | 现象 | 解决 |
|---|---|---|---|
| 1 | 5.7 升级 8.0 后 .frm 文件失效 | 表无法读取 | 8.0 数据字典统一管理,必须用 mysql_upgrade 或升级流程 |
| 2 | ibdata1 膨胀不回收 | 系统表空间越来越大 | 5.7 前 innodb_file_per_table=OFF 导致,重建库迁移 |
| 3 | innodb_flush_log_at_trx_commit=2 | 宕机丢 1 秒数据 | 生产必须设为 1 |
| 4 | doublewrite 被关闭 | 断电后页损坏无法恢复 | 保持 innodb_doublewrite=ON |
| 5 | redo log 太小 | 刷盘频繁、性能抖动 | 8.0.30+ 调大 innodb_redo_log_capacity |
| 6 | binlog 格式 STATEMENT | 主从数据不一致 | 生产统一 ROW 格式 |
| 7 | undo 表空间不够 | 长事务导致 undo 膨胀 | 监控长事务,及时 kill;调大 undo 表空间 |
| 8 | OPTIMIZE TABLE 锁表 | 业务高峰执行导致阻塞 | 低峰期执行,或用 pt-online-schema-change |
| 9 | VMware 快照回滚数据盘 | binlog 位点断裂、主从复制报错 | 数据盘设为 Independent - Persistent |
| 10 | .ibd 文件被手动复制 | 表空间导入报错 | 必须用 FLUSH TABLES ... FOR EXPORT 正规导出 |
| 11 | Buffer Pool 设太大 | 系统 OOM 或 swap | 不超过物理内存 70%,留够 OS 和连接内存 |
| 12 | 页大小不匹配 | 数据文件无法跨版本恢复 | 页大小建库后不可改,统一用默认 16KB |
十、要点速记
- InnoDB 是唯一生产选择:事务、行锁、MVCC、崩溃恢复全靠它。
- 三大日志分工:redo 保恢复(前滚)、undo 保回滚 + MVCC、binlog 保复制 + PITR。
- 双写缓冲防页半写:崩溃恢复的最后一道防线,生产必开。
- 8.0 默认 per_table 表空间:每表一个 .ibd,DROP/TRUNCATE 自动回收空间。
- Buffer Pool 是性能核心:建议物理内存 60~70%,命中率应 > 99%。
十一、参考资料(官方正式文档)
| 主题 | 官方链接 |
|---|---|
| MySQL 8.0 Reference Manual | https://dev.mysql.com/doc/refman/8.0/en/ |
| InnoDB Storage Engine | https://dev.mysql.com/doc/refman/8.0/en/innodb-storage-engine.html |
| InnoDB On-Disk Structures(表空间/页/区) | https://dev.mysql.com/doc/refman/8.0/en/innodb-on-disk-structures.html |
| InnoDB Buffer Pool | https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool.html |
| The Doublewrite Buffer | https://dev.mysql.com/doc/refman/8.0/en/innodb-doublewrite-buffer.html |
| The Change Buffer | https://dev.mysql.com/doc/refman/8.0/en/innodb-change-buffer.html |
| The Binary Log | https://dev.mysql.com/doc/refman/8.0/en/binary-log.html |
| InnoDB Redo Log | https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html |
| InnoDB Undo Logs | https://dev.mysql.com/doc/refman/8.0/en/innodb-undo-logs.html |
本文章属于《MySQL 数据库全栈运维实战系列》基础与架构篇。欢迎收藏、交流。