InnoDB 的数据存储在“空间”,MySQL 中也常被称作“表空间”,InnoDB自身则有时称其为“文件空间”。这个是个含有多个实际文件的操作系统级空间(类似 ibdata1,ibdata2 之类),它是一个逻辑文件,假如有多个实际文件那么它们在物理层面上是可拼接的。
InnoDB 中每个空间都被分配了一个 32 位的整型作为空间 ID,用于各种场景下指代这个空间。InnoDB 总是有个“系统空间”,它会被分配一个为“0”的空间 ID。系统空间是InnoDB用来存放特定的、必需的变量。在 MySQL 中,InnoDB 仅支持一种形式来添加额外的空间:每张表一个 .idb 文件。在 InnoDB 定义里,这种 .idb 文件是个全函数式空间,可以容纳多张表,但在 MySQL 的实现里,它们仅仅含有一张表。
页
每个空间通常会被分割成多个16KiB(取决于两个因素:编译时是否变更了 UNIV_PAGE_SIZE,或者是否启用了 InnoDB 压缩)的页。每个页在空间里被分配了一个32位整型的页编码,通常被称作“偏移量”,它指代了从空间的起点(假如是多文件的空间,则不能等同于某文件起点)到这个页的偏移量。因此,第 0 页位于从起点偏移量为 0 的地方,第 1 页位于偏移量为 16384 的地方,以此类推。(某些机智的人可能记起来 InnoDB 有个 64TiB 的数据量限制。这实际上说的是单个空间的限定,这主要是源自页编码,它作为一个 32 位整型,所有能表示的页总共会占用: 2^32 x 16 KiB = 64 TiB)
页的布局如下:
每个页有 38 字节的 FIL(file 的缩写) 头和 8 字节的 FIL 尾(trailer)。头拥有一个指定页类型的字段,这决定了该页接下来的结构。头和尾的结构如下:
FIL 头和尾包含如下结构:
-
32位的校验码,一个旧版的(已失效)的校验码在尾部。旧版校验码即将弃用,并在某个时候这块区域会被回收
-
页编码,页初始化时创建。检查这个字段的值和它所处的位置是否一致,有助于确定读取的正确性,且出现这个字段后即证明页的初始化已完成
-
下页指针和上页指针,这让页可以建立双向列表,这是用于索引页去链接所有同层,用于加速全索引扫描等。很多页类型并没有用这些字段。
-
64 位的日志序号(log sequence number,LSN),它指向最近一次页修改。该字段的低 32 位存储在尾部
-
页类型,它对于解析剩余页数据至关重要。页被分配给文件空间管理、区管理、事务系统、数据字典、回滚日志、blob,当然还有索引(表数据)
-
64 位的“flush LSN”,实际上只要有第 0 空间的第 0 页中,这个字段才会被填充。它存储了整个系统中(所有空间)写入任一页的最大LSN。这个字段在空间的剩余部分是个不错的可选重用对象(有更好的翻译请告知,谢谢,This field is a great candidate for re-use in the rest of the space.)
-
空间ID
空间文件
一个空间文件是许多页(最多2^32)连起来的。为了更高效的管理,页被分组到一个个1Mib(64 个连续页在默认 16KiB 时的大小)的块,被称作区(extent)。然后很多架构仅使用区在空间中分配页。
InnoDB 需要做一些账本(bookkeeping)以便追踪所有的页、区以及空间本身,所以,空间文件有一些强制性的超级结构:
空间里的首个 page(page 0)总是一个 FSP_HDR 页或叫做“文件空间头”页。FSP_HDR 页包含一个 FSP 头部结构(这点让人搞不懂),它用于跟踪一些类似空间大小,列出空闲的、碎片化的、满的区。(更多空闲空间的管理将在日后的文章里细说。)
一个 FSP_HDR 页内部仅能拥有 256 个区(或 16384 个页,共 256MiB)的空间来存储账本信息(bookkeeping information),所以每16384个页的额外必须以 XDES 页的形式预留出来,用于存储账本信息。XDES 和 FSP_HDR 的结构一模一样,只是 XDES 中 FSP 头部被清零了。这些额外的页会随着空间文件增长自动分配。
每个空间里第三个页(page 2)会成为一个 INODE 页,这会被拿来存文件段(segment,一组区外加一个单次分配片页(singly-allocated “fragment” pages)的阵列)的列表。每个 INODE 页可以存储 85 份 INODE 实体,每个索引需要两个 INODE 实体。(更多关于 INODE 实体和文件段的讨论将在日后的文章里说明。)
沿着每个 FSP_HDR 或者 XDES 页都会有一个 IBUF_BITMAP 页,这个是用来做插入缓冲的账本信息,这块内容超出了本文讨论的范围。
系统空间
在 InnoDB 中,系统空间(space 0)是个特殊的存在,它包含一些分配在固定页码上的页,存储着大量对 InnoDB 至关重要的一些信息。跟其他空间一样,系统空间有必备的头3页:FSP_HDR、IBUF_BITMAP、INODE。在此之后,有点点不一样:
以下页会被分配:
-
第3页(首个是第0页,译者注):页类型为 SYS,插入缓存的头部和账本信息
-
第4页:页类型为 INDEX,插入缓冲索引结构的根页
-
第5页:页类型为 TRX_SYS,有关系到操作 InnoDB 事务系统的信息,例如最近事务 ID 、MySQL 二进制日志信息、双写入缓冲区(double write buffer extends)的位置
-
第6页:页类型为 SYS,该页内容为首个回滚段。其他的页(或整个区)根据需要存储回滚数据
-
第7页:页类型为 SYS,数据字典的头,拥有创建数据字典索引的根页码。这个信息在寻找别的索引(表)的时候是必备的,因为它们的根页码也放在数据字典里。
-
第64-127页:第一个64页(一个区)组成的块(block)是在双写入缓冲里。双写入缓冲是 InnoDB 恢复机制里的一部分。
-
第128-191页:第2个双写入缓冲块。
其他页会按需分配给索引、回滚段、重做日志等
单表空间文件
InnoDB 提供“单表文件”模式,就是为每个 MySQL 表建一个文件(这个文件就是上述的“空间”)。对这个特性,一个更好的名字是“单表空间”。.idb 文件以下面结构创建给每一张表:
忽略“快速创建索引”在运行时加索引的情况,在3个必要的初始化页之后,接下来分配的页是表中各个索引的根页,按表创建时索引定义的顺序。第3页时聚集索引的根,第4页时首个二级键的根,等等。
当大多数 InnoDB 的账本结构存储在系统空间,大多数页按照单表空间的模式以 INDEX 的页类型来存储表数据。
下期预告
下一篇会来看 InnoDB 的空闲空间管理:区描述符、文件段(inode)、清单。