InnoDB存储引擎的逻辑存储结构和Oracle大致相同,所有数据都被逻辑地存放在一个空间中,我们称之为表空间(ta-blespace)。表空间又由段(segment)、区(extent)、页(page)组成。 表空间

表空间可以看做是InnoDB存储引擎逻辑结构的最高层,所有的数据都是存放在表空间中。默认情况下InnoDB存储引擎有一个共享表空间ibdata1,即所有数据都放在这个表空间内。如果我们启用了参数innodbfileper_table,则每张表内的数据可以单独放到一个表空间内。

表空间是由各个段组成的,常见的段有数据段、索引段、回滚段等。InnoDB存储引擎表是索引组织的(index organized),因此数据即索引,索引即数据。那么数据段即为B+树的页节点,索引段即为B+树的非索引节点。

区是由64个连续的页组成的,每个页大小为16KB,即每个区的大小为1MB。对于大的数据段,InnoDB存储引擎最多每次可以申请4个区,以此来保证数据的顺序性能。

同大多数数据库一样,InnoDB有页(page)的概念(也可以称为块),页是InnoDB磁盘管理的最小单位。与Oracle类似的是,Microsoft SQL Server数据库默认每页大小为8KB,不同于InnoDB页的大小(16KB),且不可以更改(也许通过更改源码可以)。

InnoDB存储引擎是面向行的(row-oriented),也就是说数据的存放按行进行存放。每个页存放的行记录也是有硬性定义的,最多允许存放16KB/2行的记录,即7992行记录。

InnoDB物理存储结构

从物理意义上来看,InnoDB表由共享表空间、日志文件组(更准确地说,应该是Redo文件组)、表结构定义文件组成。若将innodbfileper_table设置为on,则每个表将独立地产生一个表空间文件,以ibd结尾,数据、索引、表的内部数据字典信息都将保存在这个单独的表空间文件中。表结构定义文件以frm结尾,这个是与存储引擎无关的,任何存储引擎的表结构定义文件都一样,为.frm文件。

InnoDB行记录格式

页中保存着表中一行行的数据,InnoDB存储引擎提供了Compact和Redundant两种格式来存放行记录数据. Compact行记录格式 Compact行格式的首部是一个非NULL变长字段长度列表,而且是按照列的顺序逆序放置的。当列的长度小于255字节,用1字节表示,若大于255个字节,用2个字节表示,变长字段的长度最大不可以超过2个字节(这也很好地解释了为什么MySQL中varchar的最大长度为65535,因为2个字节为16位,即216=1=65 535)。
另外有一点需要注意的是,每行数据除了用户定义的列外,还有两个隐藏列,事务ID列和回滚指针列,分别为6个字节和7个字节的大小。若InnoDB表没有定义Primary Key,每行还会增加一个6字节的RowID列。固定长度char字段在未填充满其长度时,会用0x20来进行填充。

mysql>create table mytest ( t1 varchar(10),t2 varchar(10),t3 char(10),t4varchar(10)) engine=innodb charset=latin1 row_format=compact;

Query OK, 0 rows affected (0.00 sec)


mysql> insert into mytest values ('a','bb','bb','ccc');

Query OK, 1 row affected (0.01 sec)


mysql> insert into mytest values ('d',NULL,NULL,'fff');

Query OK, 1 row affected (0.00 sec)  

在存储行时第一行对应的结构为:

03 02 01 /变长字段长度列表,逆序/
00 /NULL标志位,第一行没有NULL值/
00 00 10 00 2c /记录头信息,固定5字节长度/
00 00 00 2b 68 00/RowID 我们建的表没有主键,因此会有RowID/
00 00 00 00 06 05/TransactionID/
80 00 00 00 32 01 10/Roll Pointer/
61/列1数据'a'/
62 62/列2 'bb'/
62 62 20 20 20 20 20 20 20 20/列3数据'bb' */
63 63 63/
列4数据'ccc'*/

第2行对应的结构为: 03 01/变长字段长度列表,逆序/
06 /NULL标志位,第三行有NULL值/
00 00 20 ff 98/记录头信息/
00 00 00 2b 68 02/RowID/
00 00 00 00 06 07/TransactionID/
80 00 00 00 32 01 10/Roll Pointer/
64/列1数据'd'/
66 66 66/列4数据'fff'/

第三行有NULL值,因此NULL标志位不再是00而是06了,转换成二进制为00000110,为1的值即代表了第2列和第3列的数据为NULL,在其后存储列数据的部分,我们会发现没有存储NULL,只存储了第1列和第4列非NULL的值。这个例子很好地说明了:不管是char还是varchar类型,NULL值是不占用存储空间的。

行溢出数据

InnoDB存储引擎可以将一条记录中的某些数据存储在真正的数据页面之外,即作为行溢出数据。
InnoDB存储引擎表是索引组织的,即B+树的结构。因此每个页中至少应该有两个行记录(否则失去了B+树的意义,变成链表了)(每行最多8098字节)。因此如果当页中只能存放下一条记录,那么InnoDB存储引擎会自动将行数据存放到溢出页中。
数据页面其实只保存了varchar(65 532)的前768个字节的前缀(prefix)数据(这里都是a),之后跟的是偏移量,指向行溢出页Uncompress BLOB Page。因此,对于行溢出数据,其存放方式如图所示。