前言
本文翻译自《Data Organization in InnoDB》,原文属于InnoDB官方blog,后来Oracle修改了官方blog的地址,没有找到原文链接,如果哪位找到原文链接,烦请告知。另本人水平有限,如发现翻译错误或有问题的地方,也欢迎指出。
简介
InnoDB会创建很多种文件,这里我们来看一下类似表空间、页、段、扇区这类的逻辑数据组织。我们将会详细介绍这几种数据组织类型,并讨论它们之间的关系。最后,我们会看一下InnoDB存储引擎内部的数据布局的高级视图。
文件
MySQL将所有数据都保存在自己的data目录中,data目录可以通过命令参数–data-dir来设置,也可以在配置文件中指定。详细请参考MySQL的命令选项介绍。
默认情况下,InnoDB在初始化的时候,会在data目录下创建三个比较重要的文件:ibdata1、ib_logfile0、ib_logfile1。其中,ibdata1文件用来保存系统数据和用户数据,ib_logfile0和ib_logfile1是redolog文件。这三个文件的存放位置以及各自的文件大小都是可配置的。可以参考InnoDB配置。
ibdata1文件属于表空间id为0的系统表空间,系统表空间可以拥有一个或多个数据文件。在MySQL 5.6版本,只有系统表空间可以拥有多个数据文件,其他所有表空间都只能有一个数据文件,而且,也只有系统表空间可以拥有多个表,其他表空间都只能有一个表。 数据文件ibdata和redolog文件ib_logfile在内存中使用C语言结构体file_node_t来表示。
表空间
默认情况下,InnoDB只包含一个ID为0的系统表空间,可以使用使用innodb_file_per_table来创建更多的系统表空间。在MySQL 5.6中,这个参数默认为ON,也就是说,每个表在自己的表空间中拥有独立的数据文件。
在InnoDB源文件storage/innobase/fil/fil0fil.cc的注释中,解释了表空间和数据文件之间的关系:
一个表空间包含了一系列的文件。由于最后的未完成的block未被使用,因此,这些文件的大小不一定会被block大小整除。当一个新文件被追加到表空间时,文件大小的最大值就已经被指定了。为了避免表空间大小不够时动态的扩展文件大小,在文件创建的时候,就把文件大小预扩展到其最大值。
上面最后一句话,避免动态扩展而预分配只应用与redolog文件,对于数据文件来说,还是动态扩展的。当然,跟前面说的一样,只有系统表空间可以包含多个数据文件。
这里需要明确一点:即使系统表空间可以包含多个数据文件,这些文件还是被当成一个串联起来的大文件使用。因此,这些文件的顺序就显得比较重要了。
页(page)
一个数据文件在逻辑上被分成多个大小一致的page,第一个数据文件的第一个page的ID标记为0,下一个page ID为1,以此类推。在一个表空间中,一个page ID(page_no)唯一标识一个page。同样,一个表空间ID(space_id)唯一标识一个表空间。因此,在InnoDB中,使用二元组(space_id, page_no)来唯一标识一个page,使用三元组(space_id, page_no, page_offset)来访问任意位置,其中page_offset是page内的字节偏移。
一个表空间中不同数据文件的各个page有什么关系呢?在源码中的另一段注释是这样描述的:
使用一个32位无符号整数来确定一个表空间中一个block的位置,由于表空间中的所有数据文件是被串联起来的,因此,在这个串联的大文件中,地址为n的block就是文件中的第n个block(第一个block地址为0,文件末尾未完成的block碎片不会被计数)。可以在这个链的尾部追加新文件来扩展表空间。
上面这段话说明,在一个表空间中,并不是所有数据文件的第一个page_no都是0,只有第一个数据文件的第一个page的page_no才为0。上面也提到,page_no是一个32位的无符号整数,所以page_no在磁盘上被存储为4个字节。
每一个page都有一个头部结构page_header_t。详细请参考另一篇blog。
扇区(extents)
一个扇区是1MB连续的page,每个扇区的大小定义为:
1 |
其中,UNIV_PAGE_SIZE是一个编译时常量,从5.6开始,是一个全局变量。一个扇区page的个数,取决于使用的page大小。如果page大小是16KB(默认),那么一个扇区就包含64个page。
page的类型
一个page有多种用途,page的类型标识着page的使用目的。page类型存储在每个page的头部中,page类型定义在源文件:storeage/innobase/include/fil0fil.h. 下面的表格,提供了page类型的简单描述。
page类型 | 描述 |
---|---|
FIL_PAGE_INDEX | 该类型page是一个B-tree节点 |
FIL_PAGE_UNDO_LOG | 该类型page存储undo log |
FIL_PAGE_INODE | 该类型page包含一组fseg_inode_t对象 |
FIL_PAGE_IBUF_FREE_LIST | 该类型page在插入buffer和修改buffer的空闲列表中 |
FIL_PAGE_TYPE_ALLOCATED | 该类型page属于新分配的page |
FIL_PAGE_IBUF_BITMAP | 该类型page保存插入buffer和修改buffer的bitmap信息 |
FIL_PAGE_TYPE_SYS | 系统page |
FIL_PAGE_TYPE_TRX_SYS | 事务系统数据 |
FIL_PAGE_TYPE_FSP_HEADER | 文件空间头部 |
FIL_PAGE_TYPE_XDES | 扇区描述符page |
FIL_PAGE_TYPE_BLOB | 未压缩的blob page |
FIL_PAGE_TYPE_ZBLOB | 第一个压缩的blob page |
FIL_PAGE_TYPE_ZBLOB2 | 子序列压缩的blob page |
每一种类型的page都有不同的用途。详细探讨每种page的用途已经超出了本文的范围。到这里为止,已经可以充分的看到,所有的page都会有一个page头部,在page头部存储着头部类型,page的类型决定的page内部数据的布局和内容格式。
表空间头部
每一个表空间,都有一个fsp_header_t类型的头部信息,这个数据结构存储在表空间的第一个page中。主要包括以下内容:
- 表空间ID(space_id)
- 表空间中page的个数
- 空闲extents链表
- 不属于任何段的完整extents链表
- 不属于任何段的部分完整或者部分空闲的extents链表
- 包含段头部的page列表,预留了所有段的inode slots
- 包含段头部的page列表,预留了非所有段的inode slots
通过表空间头部,可以直接访问表空间中可使用段的链表。表空间头部占用的字节数在宏FSP_HEADER_SIZE定义,大小等于16 × 7 = 112字节。
表空间预留page
前面已经提到,Innodb存在一个ID为0的系统表空间。这是一个特殊的表空间,在MySQL运行期间会一直保持打开状态。系统表空间的前面一些page会预留给系统内部使用。这些信息可以在头文件storage/innobase/include/fsp0types.h。下面列出一些简单的描述。
page number | page name | 描述 |
---|---|---|
0 | FSP_XDES_OFFSET | extent表述符page |
1 | FSP_IBUF_BITMAP_OFFSET | 插入buffer的bitmap page |
2 | FSP_FIRST_INODE_PAGE_NUM | 第一个inode节点的page 号 |
3 | FSP_IBUF_HEADER_PAGE_NO | 系统表空间中插入buffer的头部page |
4 | FSP_IBUF_TREE_ROOT_PAGE_NO | 系统表空间中插入buffer的root page |
5 | FSP_TRX_SYS_PAGE_NO | 系统表空间中事务系统的头部 |
6 | FSP_FIRST_RSEG_PAGE_NO | 系统表空间中第一个回滚段的page |
7 | FSP_DICT_HDR_PAGE_NO | 系统表空间中数据字段的头部page |
当配置项innodb_file_per_table开启的时候,每一个表会有一个独立的系统表空间对应一个数据文件。函数dict_build_table_def_step()—->dict_build_tablespace_for_table()中相关注释如下:
1 | /* We create a new single-table tablespace for the table. |
文件段
一个表空间包含很多文件段,文件段是一个逻辑上的概念。每个段都有一个指向该文件段inode节点(fseg_inode_t)的段头部(fseg_header_t)。文件段头部包括以下信息:
- inode属于哪个表空间
- inode的page_no
- inode偏移的字节数
- 文件段头部的长度(以字节为单位)
fseg_inode_t对象包括以下信息:
- inode所属的段id
- 完整的extents列表
- 这个段的空闲extents列表
- 部分满/空闲的extents列表
- 属于该段的独立page数组,数组的大小是一个extent的一半。
当一个段增长的时候,它会从所属的表空间中获取空闲的extent或page。
表
当一个表被创建的时候,innodb内部会创建一个BTree结构的聚簇索引。它包括连个文件段,一个存储非叶子page,另一个存储叶子page。
给定一个表,聚簇索引的Btree的root page会从数据字典中获取。因此,在innodb中,每个表存在于一个表空间中,它包括一个BTree(聚簇索引),这个BTRee有两个文件段,每个文件段可以包含很多extents,每个extent包括1MB的连续page。
小结
本文详细讨论了InnoDB内部的数据组织。首先介绍了InnoDB创建的文件类型,接着介绍了各种逻辑概念,包括表空间、page、page类型、extents、段和表,也介绍了这些逻辑概念之间的相互关系。