聊完锁机制,这期来说说 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 最重要的内存结构,缓存了磁盘上的数据页索引页

工作原理

  1. 查询数据时,先看 Buffer Pool 里有没有
    1. 有就直接返回(命中)
    1. 没有就从磁盘读取,同时放入 Buffer Pool
    1. 修改数据时,先改 Buffer Pool 的数据页,标记为「脏页」
    1. 后续由后台线程定期刷到磁盘

配置大小

-- 查看 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 的做法是:

  1. 二级索引被修改时,不立即写磁盘,而是写入 Change Buffer
    1. 后续有查询触发索引页被加载时,再合并 Change Buffer 的修改
      这大大提升了二级索引的写入性能。
-- 查看 Change Buffer 大小
SHOW VARIABLES LIKE 'innodb_change_buffer_max_size';  -- 默认 25%

Redo Log:事务的保障

Redo Log 是 InnoDB 的重做日志,用来保证事务的持久性

工作流程

  1. 事务修改数据时,先写 Redo Log(内存的 Log Buffer)
    1. 事务提交时,把 Redo Log 刷到磁盘
    1. 如果系统崩溃,重启时读取 Redo Log,重做已提交的事务
      这就是所谓的 WAL(Write-Ahead Logging)——先写日志再写数据。

配置参数

-- 查看 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 详解]
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐