InnoDB存储引擎
MySQL 的 InnoDB 存储引擎是 MySQL 数据库中最常用、功能最强大的事务型存储引擎之一。自 MySQL 5.5 版本起,InnoDB 成为 MySQL 的默认存储引擎,取代了之前的 MyISAM。InnoDB 以其对 ACID 事务的支持、行级锁定、崩溃恢复能力以及外键约束等特性,在高并发、高可靠性的应用中被广泛采用。
1. 主要特点
Section titled “1. 主要特点”1.1. 事务支持(ACID)
Section titled “1.1. 事务支持(ACID)”InnoDB 完全支持 ACID(原子性、一致性、隔离性、持久性)事务特性:
- 原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败回滚。
- 一致性(Consistency):事务执行前后,数据库从一个一致状态转移到另一个一致状态。
- 隔离性(Isolation):支持多种事务隔离级别(READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE),默认为 REPEATABLE READ。
- 持久性(Durability):一旦事务提交,其结果将永久保存,即使系统崩溃也不会丢失。
1.2. 行级锁(Row-level locking)
Section titled “1.2. 行级锁(Row-level locking)”- InnoDB 使用行级锁而非表级锁,大大提高了并发性能。
- 支持共享锁(S 锁)和排他锁(X 锁),并结合意向锁(Intention Locks)实现高效的锁管理。
- 在大多数 SELECT 语句中使用 多版本并发控制(MVCC),避免读写冲突,提升并发效率。
1.3.外键约束(Foreign Key Constraints)
Section titled “1.3.外键约束(Foreign Key Constraints)”- InnoDB 支持定义外键,确保引用完整性。
- 当插入、更新或删除涉及外键的记录时,InnoDB 自动检查约束条件,防止数据不一致。
1.4. 崩溃恢复机制(Crash Recovery)
Section titled “1.4. 崩溃恢复机制(Crash Recovery)”InnoDB 使用 redo log(重做日志) 和 undo log(回滚日志) 实现崩溃后的自动恢复。
- Redo Log:记录已提交事务的物理修改,用于恢复未写入磁盘的数据。
- Undo Log:用于回滚未提交事务,并支持 MVCC 的历史版本读取。
1.5. 多版本并发控制(MVCC)
Section titled “1.5. 多版本并发控制(MVCC)”- MVCC 允许读操作不阻塞写操作,写操作也不阻塞读操作。
- 通过保存数据的多个版本(利用 undo log)实现快照读(snapshot read)。
- 在 REPEATABLE READ 隔离级别下,InnoDB 能有效防止“幻读”(通过 Next-Key Locking)。
1.6.自适应哈希索引(Adaptive Hash Index)
Section titled “1.6.自适应哈希索引(Adaptive Hash Index)”InnoDB 的底层索引结构是 B+ 树,通常一次等值查询需要 O(log n) 的时间复杂度(需遍历树的高度)。而哈希索引支持 O(1) 的等值查找。
- InnoDB 在后台监控对 B+ 树索引 的访问模式。
- 如果发现某个索引的某些值被 频繁访问(例如,多次执行
WHERE id = 100),InnoDB 会自动在 缓冲池(Buffer Pool) 中为这些索引条目构建一个 哈希索引。 - 后续对该值的查询可以直接通过哈希表定位,无需遍历 B+ 树。
2. 存储结构
Section titled “2. 存储结构”InnoDB 的逻辑存储结构可以分为五层,从大到小依次为 表空间(Tablespace) → 段(Segment) → 区(Extent) → 页(Page) → 行(Row) 每一层都有其功能和组织方式,如下图所示:
Tablespace(表空间) └── Segment(段) ├── Data Segment(数据段,即聚簇索引) ├── Index Segment(索引段,每个二级索引一个) └── Rollback Segment(回滚段,用于 undo 日志) └── Extent(区,1MB) └── Page(页,16KB,默认) └── Row(行记录)2.1. 表空间(Tablespace)
Section titled “2.1. 表空间(Tablespace)”InnoDB 存储数据的最高物理容器,对应一个或多个磁盘文件。
类型:
-
系统表空间(System Tablespace)
-
文件名:
ibdata1,ibdata2, … -
存储:数据字典、双写缓冲(Doublewrite Buffer)、undo 日志(旧版本)、系统表等。
-
MySQL 5.7+ 默认不推荐将用户表放在此处。
-
-
独立表空间(File-Per-Table Tablespace)
-
每张用户表对应一个
.ibd文件(如users.ibd)。 -
由参数
innodb_file_per_table = ON控制(MySQL 5.6+ 默认开启)。 -
优点:便于表级备份、空间回收(
TRUNCATE/DROP可释放磁盘空间)。
-
-
通用表空间(General Tablespace)(MySQL 5.7+)
-
可通过
CREATE TABLESPACE ts1 ADD DATAFILE 'ts1.ibd'创建。 -
支持多个表共享同一个表空间文件,适用于 SSD 优化或管理需求。
-
-
临时表空间(Temporary Tablespace)
- 存储内部临时表和用户临时表(如
CREATE TEMPORARY TABLE)。
- 存储内部临时表和用户临时表(如
2.2. 段(Segment)
Section titled “2.2. 段(Segment)”在 InnoDB 存储引擎中,**段(Segment)是表空间内的逻辑分配单元,用于组织和管理同类数据。
每张表至少包含两个主要段:叶子节点段(Leaf Node Segment) 和 非叶子节点段(Non-Leaf Node Segment)。其中,叶子节点段负责存储聚簇索引的数据行,也就是表中的实际数据;而非叶子节点段则存储聚簇索引的内部节点,即 B+ 树的上层结构,用于维护索引的层级关系。
除此之外,每个二级索引也独立拥有自己的两个段,分别存储该索引的叶子节点与非叶子节点,从而保证索引结构的完整性与独立性。最后,系统还包含一个特殊的回滚段(Rollback Segment),它用于保存 undo 日志,以支持 MVCC(多版本并发控制)和事务回滚等核心功能,使得 InnoDB 能够在并发场景下保持数据的一致性与可恢复性。
2.3. 区(Extent)
Section titled “2.3. 区(Extent)”区(Extent) 是 InnoDB 存储引擎中分配存储空间的基本单位,其大小固定为 1 MB,由 64 个连续的页(每页 16 KB) 组成。
每当表中的数据量增加时,InnoDB 会以“区”为单位向段(Segment)分配空间,以实现高效的空间管理和连续的数据存储。在表刚创建或数据量较小时,可能并不会立即占用完整的区,而是仅使用其中的部分页,这种情况下的区被称为碎片区或混合区。随着数据的增长,这些混合区会逐渐演变为完整的区。
根据用途不同,区可分为多种类型:系统区用于存放系统内部信息,Undo 区用于存储回滚日志以支持事务与 MVCC,数据区用于保存表中的实际数据,索引区则专门用于存放索引结构,而空闲区则表示尚未被分配的空间。这样的设计既提高了空间利用率,也优化了数据访问的连续性和性能。
2.4. 页(Page / Block)
Section titled “2.4. 页(Page / Block)”页(Page / Block) 是 InnoDB 存储引擎中最小的 I/O 单位,也是内存与磁盘之间交互的基本载体。
默认情况下,每个页的大小为 16 KB,但可以通过参数 innodb_page_size 调整为 4K、8K 或 16K。所有的数据读写操作都是以页为单位进行的,这意味着即使只修改一行数据,InnoDB 也会将整个页加载到内存中再进行更新。
根据功能不同,页可分为多种类型:数据页(B-Tree Node) 用于存储表中的实际数据,Undo 页 存放回滚日志以支持事务与多版本控制,系统页 保存元数据,索引页 用于记录索引结构,位图页 负责管理区(Extent)内各页的分配状态,而 空闲页 则表示尚未使用的存储空间。
一个典型的数据页内部结构包括文件头(File Header)、页头(Page Header)、虚拟记录(Infimum 与 Supremum)、用户记录区(User Records)、空闲空间(Free Space)、页目录(Page Directory)以及文件尾(File Trailer)等部分。这样的分层设计既保证了页内数据的有序性,又支持快速定位与高效的空间管理。在实际应用中,一个页通常可以容纳数百行数据,具体数量取决于表中字段的大小与存储格式。
┌──────────────────────────┐│ File Header (38字节) │ ← 文件头信息│ Page Header (56字节) │ ← 页头信息│ Infimum + Supremum │ ← 虚拟最小/最大记录│ User Records │ ← 实际存储的记录│ Free Space │ ← 空闲空间│ Page Directory │ ← 记录偏移地址表│ File Trailer (8字节) │ ← 校验信息└──────────────────────────┘2.5. 行(Row)
Section titled “2.5. 行(Row)”行(Row) 是 InnoDB 存储的最小逻辑单位,对应数据库表中的一条记录。每一行记录由多个部分组成,包括记录头信息(Record Header)、实际数据字段(Columns) 以及若干隐藏列(Hidden Columns)。
记录头信息中通常包含事务 ID、回滚指针等元数据,用于支持事务一致性与并发控制;数据字段则存储用户定义的列值;而隐藏列是 InnoDB 自动维护的内部字段,其中 DB_TRX_ID 记录最后一次修改该行的事务 ID,DB_ROLL_PTR 指向相应的 Undo 日志位置,以便在事务回滚或快照读取时恢复旧版本数据,DB_ROW_ID 则是在表没有主键时由系统自动生成的隐藏主键,用于唯一标识该行。
当某一行的数据过大(例如包含 TEXT 或 BLOB 类型字段)导致超出单页容量时,就会出现 行溢出(Row Overflow) 现象。此时,InnoDB 会将超出的部分内容存放在独立的 溢出页(Overflow Page) 中,并在原页中保留指向这些溢出页的指针。通过这种机制,InnoDB 能够高效地存储和访问大字段数据,同时保持行记录结构的完整与灵活性。