【Mysql】InnoDB 引擎中的数据页结构

本文详细介绍了InnoDB存储引擎中数据页的结构,包括页的概述、数据页的16KB存储布局、记录头信息和记录间的链接方式。重点讲解了如何管理用户记录和索引页的关系,以及虚拟记录Infimum和Supremum的作用。适合深入理解InnoDB索引机制。

InnoDB 是 mysql 的默认引擎,也是我们最常用的,所以基于 InnoDB,学习页结构。而学习页结构,是为了更好的学习索引

一、页的简介

页是 InnoDB 管理存储空间的基本单位,一个页的大小一般是 16kb。

为了达成不同的目的,作者设计了多种类型的页,比如:

  • 存放表空间头部信息的页
  • 存放 change buffer 信息的页
  • 存放 inode 信息的页
  • 存放 undo 日志信息的页
  • ... ...

然而我们最关心的,还是那些存放进表中那些数据记录是在哪种页上,官方称这种存放记录的页为索引(INDEX)页,但是为了便于理解,本篇暂把它称为数据页

二、数据页的结构

这数据页也有 16kb 的存储空间,可以大致划分为 7 个部分。

从结构图中可以看到,有些部分的占用字节数是确定的,有的是不确定的。我们最关心的用户存储的记录,在 User Records部分。

不过,在一开始生成页的时候,并没有 User Records 部分。当有新的记录插入时,就会从 Free Space部分申请一个记录大小的空间,然后划分到 User Records 部分,直到 Free Space 全部被 User Records 替代,表示这个页已经用完。如果还有新的记录插入,需要申请新的页。

我觉得这里可以把这个数据页当作是书本的页,书页上的内容通常是一行行的呈现,当整个页都用完了,就得翻到下一页(新页)去继续写了。

三、记录在页中的存储结构

那么,User Records 部分里的这些记录,是如何管理的呢?

先来建一张表:

CREATE TABLE pingguo_demo(
	c1 INT,
	c2 INT,
	c3 VARCHAR(10000),
	PRIMARY KEY (c1)
	) CHARSET = ASCII ROW_FORMAT = COMPACT;

这里的指定使用行格式为 COMPACT(引擎中还存在其他的行格式),暂且知道 COMPACT 即可。

当我们在数据库的插入了一条记录后,其实背后的行格式是这样的:

注意这里橙色标识的记录头信息,它又包含了很多重要信息:

  • 预留位1:占用 1 比特,没有使用。
  • 预留位2:占用 1 比特,没有使用。
  • deleted_flag:占用 1 比特,标记该记录是否被删除。
  • min_rec_flag:占用 1 比特,在 B+ 树(后面索引会讲到)中每层非 叶子节点中的最小的目录项,都会添加此标记。
  • n_owned:一个页面中的记录被分为若干个组,每个组里有一个记录是“大哥”,其他记录都是“小弟”。而这位“大哥”记录的 n_owned 就是所在组的所有记录条数,而小弟们的 n_owned 都是 0
  • heap_no:占用 13 比特,表示当前记录在页面堆中的相对位置。
  • record_type:占用 3 比特,表示当前记录的类型,0是普通记录,1是 B+树非叶节点的目录项记录,2是 Infimum 记录,3是 Suprememum 记录。
  • next_record:占用 16 比特,表示下一条记录的相对位置。

四、记录头信息

现在,向上面新建的表中插入 4 条记录:

INSERT INTO pingguo_demo VALUES
	(1, 100, 'aaaa'),
	(2, 200, 'bbbb'),
	(3, 300, 'cccc'),
	(4, 400, 'dddd');

那么,对应这4条记录的行格式应该为:

注意,这里为了便于记忆,作了简化。另外,记录中的信息实际是二进制位数据,这里为了理解写的是十进制。而且,各条记录在 User Records 中存储是没有空隙的,这里抽象表示。

1. deleted_flag

这个属性用来标记当前记录是否被删除,1 表示被删除,0 表示没有被删除。

嗯?我表里删除了数据居然还在页里

是的,你以为被删除了,其实还在磁盘上。为什么呢?

因为如果在磁盘上移除这些记录,还要再重新排列其他记录,会带来性能消耗,所以只打了一个删除的标记。

然后,所有的删除的记录会组成一个垃圾链表。而记录在这个链表中所占用的空间称为可重用空间,当后面有新记录插入到表中,它们就可能覆盖掉这些空间。

2. min_rec_flag

在 B+ 树中每层非叶子节点中的最小的目录项,都会添加此标记。这里说的目录项,要后续讲解。

这里4条记录的 min_rec_flag 都是 0,表示都不是 B+ 树非叶子节点中的最小的目录项记录。

3. n_owned

要下一章讲解。

4. heap_no

表示当前记录在页面堆中的相对位置。

上面的4条记录是抽象的描述,实际上这些记录都是一条一条紧密无缝排列在一起的,这就是堆(heap)

为了方便管理,把一条记录在堆中的相对位置称为 heap_no。

  • 在页面前面的记录 heap_no 相对较小
  • 在页面后面的记录 heap_no 相对较大
  • 每申请一条记录的存储空间时,该记录比物理位置在它之前的那条记录的 heap_no 值大 1

上述 4 条记录的 heap_no 分别为 2、3、4、5,嗯?怎么没有 0 和 1?

虚拟记录-Infimum 和 Supremum

这个在本文第二部分有提到过。其实这2条记录是页里自动添加的:

  • Infimum:代表页面中的最小记录
  • Supremum:代表页面中的最大记录

作者规定,无论向页中插入了多少条记录,任何用户记录都比 Infimum 记录大,都比 Supremum 记录小。

这 2 条虚拟记录的结构也很简单。

所以,对于上面插入的 4 条用户记录,还应该加上这2个默认记录,而且位置最靠前。

另外,还需要注意,当堆中记录的 heap_no 值分配后,就不会发生改动。即使删除了堆中的某条记录,这条被删记录的 heap_no 值也仍然不变。

5. record_type

这个属性表示当前记录的类型,共 4 种:

  • 0:表示普通记录
  • 1:表示 B+ 树非叶节点的目录项记录
  • 2:表示 Infimum 记录
  • 3:表示 Supremum 记录
6. next_record

这个属性很重要,表示从当前记录的真实数据到下一条记录的真实数据之间的距离

  • 属性值为正数:说明当前记录的下一条记录在当前记录的后面。
  • 属性值为负数:说明当前记录的下一条记录在当前记录的前面。

比如,第 1 条记录的 next_record 值为 32,那么从此记录的真实数据地址向后找 32 字节就是下一条记录的真实数据。再比如,当值为 -111,那么就代表从此记录向前找 111 字节。

很熟悉?没错,就是链表

  • 下一条记录,是指按照主键从小到大排列的下一条。
  • Infrimum 记录的下一条记录,就是本页中主键值最小的用户记录。
  • 本页主键值最大的用户记录的下一条记录,就是 Supremum 记录。

所以,现在再来重新看下记录之间的示意图,可以用单向链表来描述了

如果这时候,删掉其中的某条记录,改变的是指针。


本文参考书籍: 小孩子4919 《mysql是怎样运行的》
<think>好的,我现在需要回答用户关于MySQL InnoDB存储引擎数据结构的问题。首先,我要回忆一下之前用户提供的引用内容,里面有关于InnoDB结构、内存和磁盘结构、索引等方面的信息。用户现在想详细了解数据结构,可能需要涵盖表结构、行格式、索引结构等部分。 首先,我需要确定InnoDB数据结构主要分为哪几部分。根据引用3,InnoDB结构分为内存和磁盘结构。但用户可能更关注磁盘上的数据结构,比如表空间、行格式、索引的实现方式。不过内存结构中的缓冲池、写缓冲等也可能影响数据处理的效率,需要简要提及。 接下来,表空间的结构。引用1提到系统表空间存储数据,还有.frm文件的元数据重叠。但InnoDBMySQL8.0之后已经将元数据存储在系统表空间,而不是.frm文件了,可能需要说明这一点。表空间包括系统表空间、用户表空间(每个表的.ibd文件)、Undo表空间和临时表空间。然后是行格式,引用2提到了CHAR(M)列的存储格式,以及当没有主键时隐藏的row_id。InnoDB支持的行格式有COMPACT、DYNAMIC等,DYNAMIC是默认的,处理可变长度列更高效。行格式的结构包括记录的额外信息和真实数据部分,比如变长字段长度列表、NULL值列表、记录头信息等。索引结构方面,引用4提到InnoDB使用B+树索引,而哈希索引只适用于特定情况。主键索引是聚簇索引,叶子节点存储完整数据,辅助索引存储主键值。需要解释B+树的结构和优势,比如范围查询、高扇出减少IO次数。另外,隐藏列如row_id,当没有主键时自动生成,作为聚簇索引的依据。事务ID和回滚指针也是行结构的一部分,支持MVCC。 内存结构中的缓冲池(BufferPool)缓存数据和索引,写缓冲(Change Buffer)优化非唯一辅助索引的更新,日志缓冲(LogBuffer)提高事务提交效率。这些虽然属于内存管理,但也影响数据访问的结构和性能。需要确保回答结构清晰,分点说明,每个部分用例子或结构图辅助理解。还要注意用户提到的LaTeX格式,所有行内数学公式用$...$,独立公式用$$...$$,比如B+树的高度计算可以用公式表示。最后,生成相关问题,可能涉及不同行格式的对比、索引优化策略、MVCC机制等。检查是否有遗漏的重要点,比如结构(16KB的,包含行记录和头信息),溢出处理(对于字段使用DYNAMIC行格式)等。确保引用正确,比如引用2提到的隐藏列,引用4的B+树结构,引用3的内存组成部分。</think>### MySQL InnoDB存储引擎数据结构详解 #### 一、表空间结构 InnoDB数据存储以**表空间**为核心,分为四类: 1. **系统表空间**:存储数据字典、双写缓冲、写缓冲等元数据[^1],默认文件为`ibdata1`。 2. **用户表空间**(.ibd文件):每个独立表对应一个文件,存储数据和索引。 3. **Undo表空间**:存储事务回滚所需的Undo日志。 4. **临时表空间**:存储临时表数据。 > **注**:MySQL 8.0后元数据完全存储在系统表空间,不再依赖`.frm`文件[^1]。 --- #### 二、行格式与存储结构 InnoDB支持种行格式,默认使用`DYNAMIC`,其核心结构如下: ```plaintext +---------------------+-------------------+-------------------+ | 变长字段长度列表 | NULL值标志位列表 | 记录头信息(6字节) | +---------------------+-------------------+-------------------+ | 事务ID(6字节) | 回滚指针(7字节) | 列数据(实际值) | +---------------------+-------------------+-------------------+ ``` 1. **隐藏列**: - 若未定义主键,自动生成`row_id`(6字节)作为聚簇索引键[^2]。 - 每行包含事务ID(`DB_TRX_ID`)和回滚指针(`DB_ROLL_PTR`),用于MVCC机制。 2. **CHAR(M)存储**: - 对于变长字符集(如UTF8),按实际长度存储,而非固定$M$字节[^2]。 3. **溢出**: - 当单行数据超过小(16KB)时,`DYNAMIC`格式仅存储20字节指针,数据存于溢出。 --- #### 三、索引结构 1. **B+树索引**: - **聚簇索引**:叶子节点存储完整数据行,物理有序。 - **辅助索引**:叶子节点存储主键值,查询需回表。 - 高度计算:若每个节点存储$n$个键,树高为$h$,则最可索引$n^{h}$条记录。例如,单存储1000键,3层B+树可支持$10^9$条记录[^4]。 2. **哈希索引**: - 仅用于自适应哈希索引(内存结构),无法替代B+树的范围查询能力[^4]。 --- #### 四、内存结构关联 1. **缓冲池(Buffer Pool)**: - 缓存数据页与索引,采用改进LRU算法管理。 - 查询时优先访问缓冲池,减少磁盘IO[^3]。 2. **写缓冲(Change Buffer)**: - 对非唯一辅助索引的更新操作(INSERT/UPDATE)进行缓存,合并写入磁盘[^3]。 3. **日志缓冲(Log Buffer)**: - 事务提交前暂存redo日志,支持三种刷盘策略(1秒刷盘/事务提交刷盘/折中模式)[^3]。 --- #### 五、示例分析 以表`record_format_demo`为例: ```sql CREATE TABLE record_format_demo (c1 INT, c2 VARCHAR(100)); ``` - 若未定义主键,实际行结构包含: - 隐藏列`row_id` - 事务ID与回滚指针 - 变长字段长度列表(记录`c2`的实际长度) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值