InnoDB数据页存储--施洪宝

一. 简介

  1. Mysql是目前最为流行的关系型数据库管理系统, 具有体积小、速度快、开放源码等优势。InnoDB是Mysql使用最广泛的存储引擎, InnoDB进行了行锁设计, 支持MVCC, 提供一致性非锁定读。学习InnoDB数据页存储, 能够让我们更加深入的理解InnoDB的一些特性。
  2. 程序 = 数据结构 + 算法, 对于Mysql而言也是如此。由于数据持久化的需要, Mysql的数据不仅存储在内存中, 也会持久化到文件中, 存储结构如下图,

InnoDB数据页存储--施洪宝_第1张图片

  • 从磁盘中, 我们可以很容易的看到持久化的各个文件。
  • 磁盘中的文件需要加载到内存中才能被程序使用, 很明显, 不可能将所有磁盘文件都加载到内存, 当内存中的数据发生更改后, 也需要刷新到磁盘文件中, 什么时候刷新, 怎么刷新, 这些都是Mysql需要考虑的问题, 但是这些内容不是本文的重点, 我们这里稍加了解即可。
  • 本文的重点是学习数据页的存储, 这些数据页可能存在与系统表空间, 独立表空间或者临时表空间。可以看到, 这些只是图中的一小部分。

3.学习之前, 我们先考虑几个问题,

  • 无论是内存存储还是磁盘存储, 都离不开内存管理, InnoDB是如何划分内存以及如何管理内存的?
  • InnoDB使用B+树存储我们表中的数据, B+树索引节点以及叶子节点应该需要存储哪些数据? 又是怎么存储的?
  • 我们在使用时, 创建了数据库, 数据表, 这些元数据是如何存储的, 查询某个表时, 如何根据元数据找到表的索引, 如何选择索引, 选择索引后, 如何定位到索引的根节点(root page)? 找到跟节点后, 又是如何一步步找到某个具体数据的?

4.说明

  • Mysql版本: 8.0.12-debug
  • 存储引擎使用InnoDB
  • 我们会用到xxd命令, 使用xxd(或者hexdump)可以以十六进制的方式查看文件。

二. InnoDB存储结构

InnoDB存储结构图如下所示, 我们这里只做简要的介绍, 更多细节我们将在后续的文章中再进行详细阐述,

InnoDB数据页存储--施洪宝_第2张图片

  1. 表空间(tablespace)可以认为是InnoDB存储引擎存储结构的最高层, 所有数据都在表空间中, 除了共享表空间外, 每个表可以创建独立表空间, 具体参数是由innodb_file_per_table参数决定, 表空间由各种段组成。
  2. 常见的段(segment)有数据段, 回滚段, 索引段。innodb中数据段就是B+树的叶子节点, 索引段就是B+树中的非叶子节点。
  3. 段是由区(extent)组成, 默认情况下区的大小是1MB, InnoDB默认页大小为16KB, 所以1个区是由16个连续页组成。
  4. innodb默认页(page)大小是16KB, 也可以通过innodb_page_size进行控制。
  5. innodb存储是面向行(row)的, 行的存储格式主要有compact、redundant、compressed、dynamic。

三. 数据页存储

3.1 独立表空间

通过innodb_file_per_table参数, 我们可以为每个表都创建一个表空间, 这个就是这个表的独立表空间, 这个表的索引段, 数据段都会存储在这个独立表空间中, 但是Redo log, Undo log仍然在各自的表空间中, 表空间存储如下图,

InnoDB数据页存储--施洪宝_第3张图片

  1. 表空间的page 0是表空间的第一页, 存储了表空间的信息, 同时也用于管理前256个extent。page 16384类型为FIL_PAGE_TYPE_XDES也用于管理之后的256个extent, 以此类推, 每隔16384个页面都会需要一个FIL_PAGE_TYPE_XDES页面。
  2. page 1类型是FIL_PAGE_IBUF_BITMAP, 用于管理每个page(前256个extent的16384个页面)的change buffer(change buffer相关内容不是本文的重点, 感兴趣的读者可以查找相关资料)。与FIL_PAGE_TYPE_XDES类似, 每隔16384个页面都需要一个FIL_PAGE_IBUF_BITMAP页面。
  3. page 2类型为FIL_PAGE_INODE, 用于管理segment。
  4. page 3类型为FIL_PAGE_SDI, 存储Serialized Dictionary Information(SDI, 词典序列化信息), 存储了这个表空间的一些数据字典(Data Dictionary)信息。
  5. page 4一般就是这个表主键索引的root page。

3.2 页存储

InnoDB的页存储结构如下, 每页都是由3部分组成, File Header(38字节)、File Body、File Trailer(8字节), 不同页的File Body存储的内容不同,

InnoDB数据页存储--施洪宝_第4张图片

1.File Header

名称 大小 说明
FIL_PAGE_SPACE_OR_CHKSUM 4字节 页的校验码
FIL_PAGE_OFFSET 4字节 表空间中页的便宜量
FIL_PAGE_PREV 4字节 上一页
FIL_PAGE_NEXT 4字节 下一页
FIL_PAGE_LSN 8字节 页面被最后修改时对应的日志序列位置
FIL_PAGE_TYPE 2字节 页面类型
FIL_PAGE_FILE_FLUSH_LSN 8字节 系统表空间中有定义, 代表文件更新到的LSN
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID 4字节 页面所属表空间id

2.File Type

名称 说明
FIL_PAGE_TYPE_ALLOCATED 0x0000 未使用
FIL_PAGE_UNDO_LOG 0x0002 undo log
FIL_PAGE_INODE 0x0003 存储了段信息
FIL_PAGE_IBUF_FREE_LIST 0x0004 Insert Buffer空闲列表
FIL_PAGE_IBUF_BITMAP 0x0005 Insert Buffer位图
FIL_PAGE_TYPE_SYS 0x0006 系统页
FIL_PAGE_TYPE_TRX_SYS 0x0007 事务系统数据
FIL_PAGE_TYPE_FSP_HDR 0x0008 表空间头部信息
FIL_PAGE_TYPE_XDES 0x0009 扩展描述页
FIL_PAGE_TYPE_BLOB 0x000A BLOB页
FIL_PAGE_SDI 0x45bd SDI索引页
FIL_PAGE_RTREE 0x45be R-tree
FIL_PAGE_INDEX 0x45bf B-tree

3.3 数据页

看完InnoDB页结构后, 我们看下数据页的存储,

InnoDB数据页存储--施洪宝_第5张图片

1.Page Header

名称 大小 说明
PAGE_N_DIR_SLOTS 2字节 page directory中slot的个数
PAGE_HEAP_TOP 2字节 堆中第一个记录指针
PAGE_N_HEAP 2字节 堆中记录数
PAGE_FREE 2字节 指向空闲空间首地址
PAGE_GARBAGE 2字节 已经删除的记录数
PAGE_LAST_INSERT 2字节 最后插入位置
PAGE_DIRECTION 2字节 最后插入方向
PAGE_N_DIRECTION 2字节 一个插入方向连续插入记录数
PAGE_N_RECS 2字节 这个页的记录总数
PAGE_MAX_TRX_ID 8字节 修改当前页的最大事务ID
PAGE_LEVEL 2字节 当前页在索引中的层, 叶子节点为0x00
PAGE_INDEX_ID 8字节 索引ID
PAGE_BTR_SEG_LEAF 10字节 非叶子节点所在段, 仅在B+树的root页中有定义
PAGE_BTR_SEG_TOP 10字节 数据页所在段, 仅在B+树的root页中有定义

2.Infimun & Supermum
虚拟记录, Infimum为13字节, Supermum也是13字节。具体存储内容, 我们会在下面进行介绍。

3.Page Directory

  • 页目录, 因为行记录在数据页中以链表的形式链接, 但是在查找记录时, 链表查找速度很慢, 为了加速记录查找, 创建页目录, 页目录可以用于二分查找。每个目录项占用2个字节, 从页尾部开始, 倒序存储。
  • 为了便于理解Page Directory, 我们这里举一个例子, 如果表中存储了200条数据, 数据通过链表的方式进行链接, 我们在查询时, 需要遍历整个链表才能找到数据, 这样无疑比较慢。我们可以通过建立索引的方式, 加快查找速度, 我们可以将这200条记录的主键按照顺序进行存储, InnoDB的Page Directory就是这个思路, 但是并不是存储了主键的值, 而是存储了对应记录的位置, 并且不是将每个行记录都存储在Page Directory中, 只是建立一个稀疏索引。

3.4 innodb行存储

限于篇幅, 我们这里主要介绍compact格式的行记录存储, 存储格式如下图,

InnoDB数据页存储--施洪宝_第6张图片

  1. 从图中可以看出, 每个记录行至少占有5字节(记录头) + 主键长度 + 6字节(事务ID) + 7字节(回滚指针)
  2. 我们需要注意记录头中的next_record字段, 这个字段占有16bit, 也就是2个字节, 通过这个字段, InnoDB将一个页中的所有记录以链表的方式链接到一起。

四. 实例讲解

为了便于大家理解, 这部分我们给出一些实例,

  1. 本节举例说明InnoDB的一个表是如何存储的, 主要介绍两种情况, 一种情况是表中数据很少, 另一种情况是表中数据比较多, 一页已经存储不了的情况。
  2. 表结构定义,
create table `t` (`id` int not null, primary key(`id`)) engine=InnoDB ROW_FORMAT=Compact;
  • 为了更容易理解, 我们这里只创建了一个非常简单的表, 也只有一个主键索引。主键类型为int, 占用4个字节。
  • 创建表后, 可以在相应的目录下看到t.ibd文件, 这里我是在test数据库下创建的这个表, 所以也就在test目录下。
  • 从磁盘文件中, 我们可以看到, t.ibd文件大小为112KB, 也就是7*16KB, 也就是7个page, 也就意味着, 创建表后, InnoDB默认初始化了7个page。
  • 我们的表中没有变长字段, 主键长度为4字节, 所以单个记录的长度为5(记录头) + 4(主键ID) + 6(事务ID) + 7(回滚指针) = 22字节

3.B+树示例
InnoDB数据存储是通过B+树组织的, 一个很简单的B+树如下所示,

InnoDB数据页存储--施洪宝_第7张图片

  • B+树的性质有很多, 其增删查改操作较常规的二叉树更复杂一些, 感兴趣的可以查询相关资料, 这里有个基本概念即可。

4.后续如果没有特殊说明, 表空间第一个页是page 0, 第二页是page 1, 以此类推。

4.1 单页存储

我们首先看下当表中数据很少的时候, 数据是如何组织的, 具体操作步骤如下,

1.我们向表中插入2条记录,

insert into t values (2);
insert into t values (1);
  • 这里注意我们先插入主键值为2的行记录, 再插入主键值为1的行记录。

2.通过xxd将t.ibd以16进制表示, 执行命令xxd t.idb t.txt, 也可以使用hexdump命令查看。

3.查看t.txt中的内容, 这里我们查看page 4的数据

0010000: a76e 6043 0000 0004 ffff ffff ffff ffff  .n`C............
0010010: 0000 0000 012e 6d9d 45bf 0000 0000 0000  ......m.E.......
0010020: 0000 0000 0005 0002 00a4 8004 0000 0000  ................
0010030: 0093 0001 0001 0002 0000 0000 0000 0000  ................
0010040: 0000 0000 0000 0000 0091 0000 0005 0000  ................
0010050: 0002 0272 0000 0005 0000 0002 01b2 0100  ...r............
0010060: 0200 3069 6e66 696d 756d 0003 000b 0000  ..0infimum......
0010070: 7375 7072 656d 756d 0000 10ff f380 0000  supremum........
0010080: 0200 0000 001c 0481 0000 00fa 0110 0000  ................
0010090: 18ff ea80 0000 0100 0000 001c 0582 0000  ................
00100a0: 012c 0110 0000 0000 0000 0000 0000 0000  .,..............
00100b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
......
......
0013fe0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0013ff0: 0000 0000 0070 0063 a76e 6043 012e 6d9d  .....p.c.n`C..m.
  • 前38字节是文件头[0010000,0010026]
  • 之后56字节是数据页头部[0010027,001005d]
  • 之后的26字节是最小记录[001005e, 001006a], 最大记录[001006b, 0010077], 这里可以看到最小记录的n_owns值为1(只有自身1条记录), 最大记录的n_owns值为3(除了自身外, 还有我们插入的两条记录)
  • 紧接着是第1条插入记录[0010078, 001008d]
0010070: .... .... .... .... 0000 10ff f380 0000
0010080: 0200 0000 001c 0481 0000 00fa 0110 ....
  • 最后是刚才插入的第2条记录[001008e, 00100a3]
  • 对于int类型, innodb存储方式与常规的方式不同, [0x00000000, 0x7fffffff]代表[-2147483648, -1], [0x80000000, 0xffffffff]代表[0, 21473647]。

4.存储结构如下图
InnoDB数据页存储--施洪宝_第8张图片

5.这里示例下如何从最小记录查找到最大记录

  • 首先定位到最小记录的位置, 最小记录占有5字节(记录头) + 8字节(内容) = 13字节, 最小记录所在的位置为0010063, 根据最小记录的记录头信息, 可以计算出下一个记录所在位置0010063 + 0030 = 0010093。这里需要注意的是, 记录所在位置使用的是内容开始的位置。
  • 0010093是主键为1的记录所在位置, 接着计算下一个记录的位置0010093 + ffea = 001007d, 这里需要注意的是, 加法运算时, 只保留后面4位的结果, 可以看到这个位置就是我们第一次插入的主键为2的记录
  • 之后, 继续计算下一个记录所在位置, 001007d + fff3 = 10070, 这个就是最大记录所在的位置
  • 在查找某个具体的行记录时, 可以先利用page directory进行近似的二分查找, 之后再进行链表查找。

6.page directory

  • 页尾部包含两个slots
0013ff0: 0000 0000 0070 0063 .... .... .... ....
  • 0063是第1个slot的位置, 相应的记录所在位置为0010063, 也就是最小记录。[001005e, 0010062]这个是最小记录的记录头, [0010063, 001006a]是最小记录的内容。
  • 0070是第2个slot的位置, 相应的记录所在位置为0010070, 这个是最大记录所在的内容开始位置。

7.小结

  • 可以看到, 从最小记录开始, 到最大记录结束, 数据按照主键顺序以链表的方式进行链接。
  • 行数据的存储是按照插入的顺序存储的, 不是按照主键顺序存储, 数据删除后, 释放的空间可以复用, 关于复用部分的细节, 后续文章再进行详细介绍。

4.2 多页存储

在4.1的基础上, 我们继续插入数据, 操作步骤如下,

  1. 我们通过脚本向表中继续插入数据
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    for($i = 3; $i < 1000; $i++){
        $sql = "INSERT INTO t VALUES (" . strval($i) . ")";
        $conn->exec($sql);
    }
}
catch(PDOException $e){
    echo $sql . "
" . $e->getMessage(); } $conn = null; ?>
  • 之前表中已经插入2条记录, 这里又插入997条记录, 所以表中现在一共999条记录, 主键id从1到999。
  • 单条记录需要占用22字节, 可以知道, 此时, 单个数据页不能存储全部数据。

2.以16进制查看此时的t.ibd文件: xxd t.ibd t.txt
3.查看t.txt内容, 首先查看page 4的内容

0010000: df67 193d 0000 0004 ffff ffff ffff ffff  .g.=............
0010010: 0000 0000 0132 5500 45bf 0000 0000 0000  .....2U.E.......
0010020: 0000 0000 0005 0002 0092 8004 0000 0000  ................
0010030: 008a 0002 0001 0002 0000 0000 0000 0000  ................
0010040: 0001 0000 0000 0000 0091 0000 0005 0000  ................
0010050: 0002 0272 0000 0005 0000 0002 01b2 0100  ...r............
0010060: 0200 1a69 6e66 696d 756d 0003 000b 0000  ...infimum......
0010070: 7375 7072 656d 756d 1000 1100 0d80 0000  supremum........
0010080: 0100 0000 0500 0019 ffe6 8000 0153 0000  .............S..
0010090: 0006 0000 0000 0000 0000 0000 0000 0000  ................
.......
.......
0013fe0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0013ff0: 0000 0000 0070 0063 df67 193d 0132 5500  .....p.c.g.=.2U.
  • 可以看到, 第5页, 目前只有少量内容, 因为此时第5页是索引页, 是B+树的根, 没有存储具体的数据, 只存储了主键索引。
  • File Header, Page Header, Infimum & Supremum跟之前基本类似, 这里就不再详细介绍。
  • 单个索引需要占用5字节(记录头) + 4字节(主键) + 4字节(记录所在页) = 13字节。
  • 第1个索引信息
0010070: .... .... .... .... 1000 1100 0d80 0000
0010080: 0100 0000 05.. .... .... .... .... ....

主键id为0x80000001, 也就是1, page no为0x00000005, 也就是page 5

  • 第2个索引信息
0010080: .... .... ..00 0019 ffe6 8000 0153 0000
0010090: 0006 .... .... .... .... .... .... ....

主键id为0x80000153, 也就是339, page no为0x00000006, 也就是page 6

  • 通过这两个索引信息, 可以知道, page 5存储着主键id从1到338的数据, page 6存储着主键id从339到999的数据

4.查看page 5

0014000: e1c0 bb7a 0000 0005 ffff ffff 0000 0006  ...z............
0014010: 0000 0000 0132 5500 45bf 0000 0000 0000  .....2U.E.......
0014020: 0000 0000 0005 0056 3a90 82a6 1d89 1d0c  .......V:.......
0014030: 0000 0005 0000 0152 0000 0000 0000 0000  .......R........
0014040: 0000 0000 0000 0000 0091 0000 0000 0000  ................
0014050: 0000 0000 0000 0000 0000 0000 0000 0100  ................
0014060: 0200 1a69 6e66 696d 756d 0003 000b 0000  ...infimum......
0014070: 7375 7072 656d 756d 0000 1000 1680 0000  supremum........
0014080: 0100 0000 001c 0582 0000 012c 0110 0000  ...........,....
0014090: 1800 1680 0000 0200 0000 001c 0481 0000  ................
00140a0: 00fa 0110 0000 2000 1680 0000 0300 0000  ...... .........
.......
.......
0017e90: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0017ea0: 0000 0000 0070 3a27 39cf 3977 391f 38c7  .....p:'9.9w9.8.
0017eb0: 386f 3817 37bf 3767 370f 36b7 365f 3607  8o8.7.7g7.6.6_6.
0017ec0: 35af 3557 34ff 34a7 344f 33f7 339f 3347  5.5W4.4.4O3.3.3G
0017ed0: 32ef 3297 323f 31e7 318f 3137 30df 3087  2.2.2?1.1.170.0.
0017ee0: 302f 2fd7 2f7f 2f27 2ecf 2e77 2e1f 2dc7  0//././'...w..-.
0017ef0: 2d6f 2d17 2cbf 2c67 2c0f 2bb7 2b5f 2b07  -o-.,.,g,.+.+_+.
0017f00: 2aaf 2a57 29ff 29a7 294f 28f7 289f 2847  *.*W).).)O(.(.(G
0017f10: 27ef 2797 273f 26e7 268f 2637 25df 2587  '.'.'?&.&.&7%.%.
0017f20: 252f 24d7 247f 2427 23cf 2377 231f 22c7  %/$.$.$'#.#w#.".
0017f30: 226f 2217 21bf 2167 210f 20b7 205f 2007  "o".!.!g!. . _ .
0017f40: 1faf 1f57 1eff 1ea7 1e4f 1df7 0070 1d47  ...W.....O...p.G
0017f50: 1cef 1c97 1c3f 1be7 1b8f 1b37 1adf 1a87  .....?.....7....
0017f60: 1a2f 19d7 197f 1927 18cf 1877 181f 17c7  ./.....'...w....
0017f70: 176f 1717 16bf 1667 160f 15b7 155f 1507  .o.....g....._..
0017f80: 14af 1457 13ff 13a7 134f 12f7 129f 1247  ...W.....O.....G
0017f90: 11ef 1197 113f 10e7 108f 1037 0fdf 0f87  .....?.....7....
0017fa0: 0f2f 0ed7 0e7f 0e27 0dcf 0d77 0d1f 0cc7  ./.....'...w....
0017fb0: 0c6f 0c17 0bbf 0b67 0b0f 0ab7 0a5f 0a07  .o.....g....._..
0017fc0: 09af 0957 08ff 08a7 084f 07f7 079f 0747  ...W.....O.....G
0017fd0: 06ef 0697 063f 05e7 058f 0537 04df 0487  .....?.....7....
0017fe0: 042f 03d7 037f 0327 02cf 0277 021f 01c7  ./.....'...w....
0017ff0: 016f 0117 00bf 0063 e1c0 bb7a 0132 5500  .o.....c...z.2U.
  • 注意页尾部包含page directory, slots的个数可以从page header中读取
  • File Header中的FIL_PAGE_NEXT字段, 值为0x00000006, 也就是page no为6的页。

5.查看page 6

0018000: 2ddb 788c 0000 0006 0000 0005 ffff ffff  -.x.............
0018010: 0000 0000 0133 f431 45bf 0000 0000 0000  .....3.1E.......
0018020: 0000 0000 0005 00a6 3946 8297 0000 0000  ........9F......
0018030: 3935 0002 0142 0295 0000 0000 0000 0000  95...B..........
0018040: 0000 0000 0000 0000 0091 0000 0000 0000  ................
0018050: 0000 0000 0000 0000 0000 0000 0000 0100  ................
0018060: 0200 1a69 6e66 696d 756d 0006 000b 0000  ...infimum......
0018070: 7375 7072 656d 756d 0000 1000 1680 0001  supremum........
0018080: 5300 0000 001d 6d81 0000 00a3 0110 0000  S.....m.........
.......
.......
001bea0: 0000 0000 0000 0000 0000 0000 0070 38c7  .............p8.
001beb0: 386f 3817 37bf 3767 370f 36b7 365f 3607  8o8.7.7g7.6.6_6.
001bec0: 35af 3557 34ff 34a7 344f 33f7 339f 3347  5.5W4.4.4O3.3.3G
001bed0: 32ef 3297 323f 31e7 318f 3137 30df 3087  2.2.2?1.1.170.0.
001bee0: 302f 2fd7 2f7f 2f27 2ecf 2e77 2e1f 2dc7  0//././'...w..-.
001bef0: 2d6f 2d17 2cbf 2c67 2c0f 2bb7 2b5f 2b07  -o-.,.,g,.+.+_+.
001bf00: 2aaf 2a57 29ff 29a7 294f 28f7 289f 2847  *.*W).).)O(.(.(G
001bf10: 27ef 2797 273f 26e7 268f 2637 25df 2587  '.'.'?&.&.&7%.%.
001bf20: 252f 24d7 247f 2427 23cf 2377 231f 22c7  %/$.$.$'#.#w#.".
001bf30: 226f 2217 21bf 2167 210f 20b7 205f 2007  "o".!.!g!. . _ .
001bf40: 1faf 1f57 1eff 1ea7 1e4f 1df7 1d9f 1d47  ...W.....O.....G
001bf50: 1cef 1c97 1c3f 1be7 1b8f 1b37 1adf 1a87  .....?.....7....
001bf60: 1a2f 19d7 197f 1927 18cf 1877 181f 17c7  ./.....'...w....
001bf70: 176f 1717 16bf 1667 160f 15b7 155f 1507  .o.....g....._..
001bf80: 14af 1457 13ff 13a7 134f 12f7 129f 1247  ...W.....O.....G
001bf90: 11ef 1197 113f 10e7 108f 1037 0fdf 0f87  .....?.....7....
001bfa0: 0f2f 0ed7 0e7f 0e27 0dcf 0d77 0d1f 0cc7  ./.....'...w....
001bfb0: 0c6f 0c17 0bbf 0b67 0b0f 0ab7 0a5f 0a07  .o.....g....._..
001bfc0: 09af 0957 08ff 08a7 084f 07f7 079f 0747  ...W.....O.....G
001bfd0: 06ef 0697 063f 05e7 058f 0537 04df 0487  .....?.....7....
001bfe0: 042f 03d7 037f 0327 02cf 0277 021f 01c7  ./.....'...w....
001bff0: 016f 0117 00bf 0063 2ddb 788c 0133 f431  .o.....c-.x..3.1
  • 注意File Header中的FIL_PAGE_PREV字段, 值为0x00000005, 也就是page no为5的页。
  • 结合page 5可以看出, 叶子节点的两个页通过链表进行链接, 每个页内的数据通过记录头中的next_record字段进行链接。

6.存储结构图如下,
InnoDB数据页存储--施洪宝_第9张图片

7.小结

  • 对于单页存储不了的情况, 需要进行页分裂, 此时B+树会有多层结构, 最低层为叶子节点, 存储了具体的数据, 上面是索引节点, 只存储主键以及下一层节点所在的页信息

五. 总结与思考

本文介绍了innodb的数据页存储, 以实例的方式讲解了innodb存储引擎如何存储一个表中数据的。但是我们仍然有很多问题没有给出答案,

  1. 查找行记录时, 需要找到某个索引的root page, 这个信息是存储在哪里的?
  2. 我们没有介绍段和区的相关内容, 这些在InnoDB数据存储时是如何使用的?
  3. 我们查看数据时, 都是直接查看磁盘文件, 内存中的页与磁盘中的页有何区别, 内存中的脏页又是如何刷新到磁盘的?

InnoDB存储引擎较为复杂, 不可能一次性将全部内容学会, 我们不妨每次带入一个问题, 深入寻找这个问题的答案, 关于这些问题, 我会在后续文章中再逐步介绍。

六. 参考

  1. <>
  2. 淘宝数据库内核月报

你可能感兴趣的