InnoDB空间文件结构基础内容
在《学习InnoDB的必经之路》这篇文章中,我介绍了记录InnoDB内部数据结构的项目–innodb_diagrams,该项目提供了这篇文章所用的图解。
InnoDB的数据存储模块使用的“space”(空间),在MySQL上下文中通常被称之为“tablespaces”(表空间),有些时候InnoDB本身称之为”file spaces“(文件空间)。一个”space“(空间)可能由多个操作系统级别的(比如 ibdata1,ibdata2,等等)实际的文件组成,但它逻辑上是一个文件–多个物理文件被视为物理上连接在一起。
在InnoDB内部会为每个空间分配一个32位整数的空间ID(“32-bit integer space ID”),它被用于众多不同的地方并指向这片空间。InnoDB总会有个”system space(系统空间)“,它总是被分配为0的空间ID(“space ID”)。系统空间总是用于InnoDB所需的各种特殊薄记(“bookkeeping”1)。目前,MySQL InnoDB引擎仅支持以“file pre table(每个表一个文件)”形式的额外空间,它为MySQL的每一个表创建一个.ibd文件。.ibd文件内部实际上是一个功能齐全的空间,可以包含多张表,但是MySQL实现的.ibd文件仅包含一张表。
基础的页结构
每一个空间被分成多页,每页16KiB大小(改变大小的唯二方式:1.编译时定义的UNIV_PAGE_SIZE
参数发生改变。2.使用了InnoDB压缩)。空间中每页被分配一个32位整数的页编号(“32-bit integer page number”),通常称之为”offset“(偏移量),事实上是相对于空间开始位置的页偏移量(对于多文件空间来说,不一定是文件的开始位置)。因此,页编号(“page number”)为0的位置是文件偏移量为0的地方,而页编号为1的位置是文件偏移量为163842的地方,以此类推。(有些人可能记得InnoDB有64TiB数据大小的限制,事实上是每个空间的限制,主要是由于页编号是一个32位的整数,它和默认页大小结合即:232 x 16KiB=64TiB)
基础页结构如下图:
每页均有一个38 Byte的“FIL header(文件头部)”和8 Byte的“FIL Trailer(文件尾部,FIL是”file“的缩写)”。头部包含了一个用于表明页类型的字段,申明了页后续部分的数据结构。“FIL header”和“FIL Trailer”的结构如下:
“FIL header”和“FIL Trailer”包含以下数据结构(无序):
- 页类型(“Page Type”)存储在头部中。页类型可表明该页是什么类型,对于解析页后续数据有所帮助。页被用于文件空间管理,区管理,事务系统,数据字典,回滚日志,二进制和索引(表数据)(“file space management, extent management, the transaction system, the data dictionary, undo logs, blobs, and of course indexes (table data)”)。
- 空间ID(“Space ID”)存储于头部中。
- 页编号(“Page Number”)存储于头部中。只要page初始化后就生成该数据。检查从该字段中读取的页编号(“page number”)与基于文件中的偏移量是否匹配,可表明读取是否正确,并且该字段初始化后也表明页(“page”)被初始化了。
- 32位的“校验和”(“32-bit checksum”)被存储在头部和尾部,它是一种较老的(和碎片化的)格式,会在将来某个时间点遗弃掉并回收该部分空间。
- 该页逻辑上的上一页与下一页被存储在头部中。这样可以构建页的双向链表,索引页用它来链接所有相同等级的页,并且使得例如全索引扫描更高效。大多数的页(其他类型的页)不会用到该字段。
- 页最后修改的64位日志序列号(“log sequence number”:缩写为LSN)存储于头部中。同样的LSN低32日志序列号位存储于尾部中。
- 64位的”flush LSN“存储于头部中,在整个系统中实际上仅存在于一个页中,即0号空间的0页(page 0 of space 0)。这个字段存储了整个系统(所有空间(all space))刷新到任何页面的最高级别LSN3。这个字段在空间剩下的部分中是个绝佳的可重用候选项。
空间文件结构总览(Space file)
一个空间文件只是串联了许多页(上限为232)。为了更高效的管理,许多页按照1MiB大小分为一组(64个连续16KiB大小的页),被称之为“extent”(数据段)。大多数的数据结构只引用数据段(extent),而数据段早已分配好空间中的页(page)。
InnoDB需要做一些簿记来追踪所有的页,数据段以及空间本身,因此空间文件有一些强制性的上层结构(super-structure)。
第0页(page编号为0的位置)总是FSP_HDR或者文件空间头(“file space header”)页。FSR_HDR页包含了(令人困惑的)FSP头部数据结构,它会追踪诸如空间的大小,可用空间列表,碎片化的空间和完整的数据段(“full extents”,应当指的是整个数据段是连续空间组成的)(关于可用空间管理的细节将会在后续文章中)。
一个FSP_HDR页仅有足够的内部空间为256个数据段(“extents”)(或16384页数据256MiB)存储簿记信息(“bookkeeping information”),因此,其他空间必须以XDES页的形式保存所有的16384页的簿记信息。XDES和FSP_HDR的结构相同,区别在于XDES页中FSP头部结构被清除。额外的页随着空间文件的增长自动分配。
第2页(page编号为2)都是INODE页,用于存储文件段相关的列表(一组数据段(“grouping of extents“)加上单独分配的碎片页数组(”an arrays of singly-allocated ‘fragment’ pages“))。每个INODE页可以存储85个INODE条目,并且每个索引需要两个INODE条目。(关于INODE条目和文件段的更多细节将放在后续文章中)
除了FSP_HDR或XDES页之外,还有一个是IBUF_BITMAP页,它用于插入缓冲相关的簿记信息,这在本文讨论范围之外。
系统空间文件结构(System space file)
系统空间(空间编号为0的,space ID = 0)在InnoDB中较为特殊,包含了相当多的以固定页编号分配的页,这些页存储InnoDB操作相关的各类重要信息。由于系统空间和其他空间类似,在它的前三页分配FSP_HDR页,IBUF_BITMAP页以及INODE页。后续页就有些不同:
页所分配内容如下:
- 第3页(page编号为3),类型为“SYS”,插入缓冲相关的头和簿记信息。
- 第4页(page编号为4),类型为“INDEX”,插入缓冲索引结构的根页。
- 第5页(page编号为5),类型为“TRX_SYS”,InnoDB的事务系统相关的操作信息,例如最新的事务ID,MySQL的binlog信息,还有双写缓冲区的位置。
- 第6页(page编号为6),类型为“SYS”,第一个回滚段页。根据需要分配额外的页来存储回滚段数据。
- 第7页(page编号为7),类型为“SYS",与数据字典相关的头部(”Headers“)包含了生成该数据字典的索引的根节点页编号。可根据该信息找到任何其他的索引(表),因为它们的根节点页编号存储在数据字典本身中。
- 第64到127页(page编号从64到127),第一个由64个页(一个数据段)组成的块,存在于双写缓存中。
- 第128页到191页(page编号从128到191),第二个双写缓存块。
剩余其他的所有页根据需要分配给索引,回滚段,回滚日志等等。
每个表拥有一个空间文件
InnoDB提供了”file per table(每个表一个文件)“模式,它会为每个创建好的MySQL表创建一个文件(如前面所述,实际上它是一个空间)。这个特性更合适的名字应该是”space per table(每个表一个空间)“而不是”file per table(每个表一个文件)“。为每个表所创建.ibd文件有着典型的空间文件结构:
先别管”fast index creation(快速索引创建)“,它在运行时添加索引并且新添加的索引页一定是在前面3个页之后,在空间中分配的下一页(第3页,page 3)将会是该表所有索引的根页,以它们在表创建中所定义的顺序4。第4页(page编号:3)将会是聚簇索引的根页,而第5页(即page编号:4)将会是第一个次要索引的根页,以此类推。
大多数的InnoDB簿记结构存储在系统空间(”system space“)中,所以大多数”space per table(每个表一个空间)“中分配的页都将是索引类型(”type INDEX“)且存储表数据。
下文会讲什么
后面一篇文章将会看看InnoDB中可用空间管理:数据段描述符(”extent descriptors“),文件段(”inodes“),以及列表(”lists“)。
注释
- [1]:来自open-api-chatGPT:在MySQL中,“bookkeeping”(簿记)通常指的是记录和维护数据库内部状态和元数据的活动。这包括但不限于跟踪数据页的使用情况、记录事务信息、管理索引结构、维护空闲空间信息等。
- 具体而言,“bookkeeping” 涉及以下方面:
- 空间管理: 记录和管理磁盘上数据和索引的分布,包括页的分配和释放,以及对空间的使用情况进行跟踪。
- 事务管理: 记录事务的开始、提交和回滚,以维护数据库的一致性和隔离性。
- 索引结构: 维护和更新索引结构,以便数据库查询可以高效地执行。
- 元数据管理: 记录数据库对象(如表、列、索引等)的元数据信息,以便数据库引擎可以正确解释和处理数据。
- 在数据库系统中,簿记活动对于确保数据库的正确运行和性能至关重要。这些信息通常存储在特定的系统表或系统页中,以供 数据库引擎在需要时查询和更新。
- [2]:page 1 :一个page的大小为16KiB,即16x1024 Byte = 16384 Byte。这里的文件偏移量实际是指从文件开始位置(即 0 Byte位置)到该页之间有多少个Byte。page 0 的开始位置在 0 Byte位置,向下推移16383 Byte就是page 0 整页所占用的空间。page 1的开始位置就从page 0末尾开始也就是16383 Byte之后即16834 Byte的位置。
- [3]:LSN:是"log sequence number"的缩写。该句含义我还没完全理解到位,所以翻译可能有些出入。
- [4]:该句的含义有些模糊。