MySQL InnoDB 存储引擎详解
聊完锁机制,这期来说说 MySQL 的存储引擎。存储引擎是 MySQL 区别于其他数据库的核心特色——你可以根据不同业务场景,选择不同的存储引擎。
MySQL 存储引擎有哪些?
-- 查看 MySQL 支持哪些存储引擎
SHOW ENGINES;
常见的存储引擎:
| 存储引擎 | 事务 | 锁粒度 | 外键 | 说明 |
|---|---|---|---|---|
| InnoDB | ✅ | 行锁 | ✅ | 默认引擎,支持事务和行锁 |
| MyISAM | ❌ | 表锁 | ❌ | 早期常用,不支持事务 |
| Memory | ❌ | 行锁 | ❌ | 内存表,重启数据丢失 |
| Archive | ❌ | 行锁 | ❌ | 归档引擎,只支持 INSERT/SELECT |
| NDB | ✅ | 行锁 | ✅ | MySQL 集群专用 |
现在绝大多数场景都用 InnoDB,这也是本文的重点。
InnoDB 架构
InnoDB 的架构比较复杂,分为两层:内存结构和磁盘结构。
内存结构
┌─────────────────────────────────────────┐
│ Buffer Pool │ ← 缓冲池,缓存数据页
├─────────────────────────────────────────┤
│ Change Buffer │ ← 变更缓冲,优化二级索引
├─────────────────────────────────────────┤
│ Log Buffer │ ← 日志缓冲,redo log 写前缓冲
└─────────────────────────────────────────┘
磁盘结构
┌─────────────────────────────────────────┐
│ 系统表空间 (ibdata1) │ ← 共享表空间
├─────────────────────────────────────────┤
│ 独立表空间 (.ibd) │ ← 每个表单独文件
├─────────────────────────────────────────┤
│ redo log (ib_logfile0/1) │ ← 重做日志
├─────────────────────────────────────────┤
│ undo log │ ← 回滚日志
└─────────────────────────────────────────┘
Buffer Pool:核心缓存
Buffer Pool 是 InnoDB 最重要的内存结构,缓存了磁盘上的数据页和索引页。
工作原理
- 查询数据时,先看 Buffer Pool 里有没有
-
- 有就直接返回(命中)
-
- 没有就从磁盘读取,同时放入 Buffer Pool
-
- 修改数据时,先改 Buffer Pool 的数据页,标记为「脏页」
-
- 后续由后台线程定期刷到磁盘
配置大小
-- 查看 Buffer Pool 大小
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- 生产环境建议设置为可用内存的 60-80%
-- 比如 32G 内存,就设置为 20G 左右
SET GLOBAL innodb_buffer_pool_size = 21474836480; -- 20GB
缓存淘汰:LRU
Buffer Pool 空间有限,需要淘汰不常用的数据页。InnoDB 用的是改进的 LRU 算法:
[新鲜数据区] [冷数据区]
63% + 37%
- 新读取的页放入「新鲜数据区」(63%)
-
- 冷数据被赶到「冷数据区」(37%)
-
- 冷数据区太久没被访问就会被淘汰
Change Buffer:二级索引的优化
Change Buffer 是 InnoDB 对二级索引的特殊优化。
二级索引有个特点:顺序性不强,插入随机。直接刷盘会很慢。Change Buffer 的做法是:
- 二级索引被修改时,不立即写磁盘,而是写入 Change Buffer
-
- 后续有查询触发索引页被加载时,再合并 Change Buffer 的修改
这大大提升了二级索引的写入性能。
- 后续有查询触发索引页被加载时,再合并 Change Buffer 的修改
-- 查看 Change Buffer 大小
SHOW VARIABLES LIKE 'innodb_change_buffer_max_size'; -- 默认 25%
Redo Log:事务的保障
Redo Log 是 InnoDB 的重做日志,用来保证事务的持久性。
工作流程
- 事务修改数据时,先写 Redo Log(内存的 Log Buffer)
-
- 事务提交时,把 Redo Log 刷到磁盘
-
- 如果系统崩溃,重启时读取 Redo Log,重做已提交的事务
这就是所谓的 WAL(Write-Ahead Logging)——先写日志再写数据。
- 如果系统崩溃,重启时读取 Redo Log,重做已提交的事务
配置参数
-- 查看 redo log 大小
SHOW VARIABLES LIKE 'innodb_log_file_size'; -- 默认 48MB
-- 查看 redo log 文件数量
SHOW VARIABLES LIKE 'innodb_log_files_in_group'; -- 默认 2
-- 建议:redo log 总大小 = Buffer Pool 的 1/4 到 1/2
Undo Log:回滚的保障
Undo Log 用来实现事务回滚和MVCC。
- 事务修改数据前,先把原数据写入 Undo Log
-
- 事务回滚时,读取 Undo Log 恢复数据
-
- MVCC 读取历史版本时,也是通过 Undo Log
表空间:数据怎么存?
系统表空间
默认情况下,所有表的数据都存在 ibdata1 这个共享表空间里。
-- 查看系统表空间位置
SHOW VARIABLES LIKE 'innodb_data_file_path';
独立表空间
MySQL 5.6.6 之后,默认启用独立表空间,每个表单独一个 .ibd 文件。
-- 查看是否启用独立表空间
SHOW VARIABLES LIKE 'innodb_file_per_table'; -- 默认 ON
独立表空间的好处:
- 单表删除可以释放空间
-
- 单表备份更方便
-
- 更容易监控单表大小
统计信息:优化器的依据
InnoDB 会定期统计表和索引的基数(cardinality),供优化器生成执行计划。
-- 手动更新统计信息(生产环境慎用)
ANALYZE TABLE user;
常见问题
1. 为什么表数据删除后文件大小不变?
因为 Buffer Pool 里还有缓存,磁盘文件不会立即收缩。
-- 释放空间(InnoDB 1.2+)
OPTIMIZE TABLE user; -- 会重建表,释放空间
2. Redo Log 写满了怎么办?
Redo Log 是循环使用的。如果写满了,会阻塞所有更新操作,强制刷脏页。
解决:增大 redo log 文件大小,或者提高刷盘频率。
3. Buffer Pool 命中率低怎么办?
-- 查看缓存命中率
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
-- 计算:1 - (物理读取 / 总读取)
-- 命中率低于 95% 就要关注了
总结
| 组件 | 作用 |
|---|---|
| Buffer Pool | 缓存数据页和索引页,是 InnoDB 的核心 |
| Change Buffer | 优化二级索引的写入 |
| Redo Log | 保证事务持久性,重做已提交事务 |
| Undo Log | 保证事务原子性,回滚未提交事务 |
| 表空间 | 数据存储,物理文件 |
InnoDB 是 MySQL 最核心的存储引擎,搞懂它的架构原理,对于调优和排查问题都很有帮助。下期咱们聊聊如何分析执行计划,看看查询有没有走索引。
相关阅读:
- [MySQL 索引底层 B+ 树原理]
-
- [MySQL 锁机制完全指南]
-
- [MySQL 执行计划 EXPLAIN 详解]
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)