目录
表
索引组织表
InnoDB逻辑存储结构
表空间
段
区
页
行
表
表就是关于特定实体的数据集合,这是关系型数据库模型的核心。
索引组织表
在InnoDB存储引擎中,表都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(index organized table)。在 InnoDB 存储引擎表中,每张表都有个主键(Primary Key),如果在创建表时没有显式地定义主键,则 InnoDB 存储引擎会按如下方式选择或创建主键:
- 首先判断表中是否有非空的唯一索引(Unique NOT NULL),如果有,则该列即为主键。
- 如果不符合上述条件, InnoDB存储引擎自动创建一个6字节大小的指针。
当表中有多个非空唯一索引时,InnoDB 存储引擎将选择建表时第一个定义的非空唯一索引为主键。这里需要非常注意的是,主键的选择根据的是定义索引的顺序,而不是建表时列的顺序。看下面的例子:
CREATE TABLE z(a INT NOT NULL,b INT NULL,c INT NOT NULL,d INT NOT NULL,UNIQUE KEY(b),UNIQUE KEY(d),UNIQUE KEY(c)
)ENGINE=INNODB,CHARSET=utf8;//插入几条数据
INSERT INTO z SELECT 1,2,3,4;
INSERT INTO z SELECT 5,6,7,8;
INSERT INTO z SELECT 9,10,11,12;
上述示例创建了一张表 z,有a、b、c、d 四个列。b、c、d 三列上都有唯一索引,不同的是 b 列允许 NULL 值。由于没有显式地定义主键,因此会选择非空的唯一索引,可以通过下面的SQL语句判断表的主键值:
//查询情况,_rowid可以判断主键是哪一列
SELECT a,b,c,d,_rowid FROM z;

_rowid可以显示表的主键,因此通过上述查询可以找到表z的主键。此外,虽然c、d列都是非空唯一索引,都可以作为主键的候选,但是在定义的过程中,由于d列首先定义为唯一索引,故 InnoDB 存储引擎将其视为主键。另外需要注意的是_rowid只能用于查看单个列为主键的情况,对于多列组成的主键就显得无能为力了,如:
//当遇到下面的建表语句是无法使用_rowid查询的
CREATE TABLE z2(a INT NOT NULL,b INT NOT NULL,c INT NOT NULL,PRIMARY KEY(a,b)
)ENGINE=INNODB,CHARSET=utf8;
InnoDB逻辑存储结构
从 InnoDB 存储引擎的逻辑存储结构看,所有数据都被逻辑地存放在一个空间中,称之为表空间(tablespace)。表空间又由段(segment)、区(extent)、页(page)组成。页在一些文档中有时也称为块(block), InnoDB 存储引擎的逻辑存储结构大致如图4-1 所示。

表空间
表空间可以看做是 InnoDB 存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。前面已经介绍了在默认情况下 InnoDB 存储引擎有一个共享表空间 ibdatal,即所有数据都存放在这个表空间内。
如果用户启用了参数innodb_file_per_table,则每张表内的数据可以单独放到一个表空间内。如果启用了innodb_file_per_table的参数,需要注意的是每张表的表空间内存放的只是数据、索引和插入缓冲Bitmap页,其他类的数据,如回滚(undo)信息,插入缓冲索引页、系统事务信息,二次写缓冲(Double write buffer)等还是存放在原来的共享表空间内。
这同时也说明了另一个问题:即使在启用了参数innodb_file_per_table之后,共享表空间还是会不断地增加其大小。
段
表空间是由各个段组成的,常见的段有数据段、索引段、回滚段等。
数据段其实就是InnoDB索引底层实现结构的B+树的叶子结点(leaf node segment),索引段即为B+树的非索引结点(Non-leaf node segement),回滚段则比较特殊,后面在介绍
在InnoDB存储引擎中,对段的管理都是由引擎自身所完成的,用户没有权限去进行管理,同时也没有必要去管理。
区
区是由连续页组成的空间,在任何情况下每个区的大小都为1MB。为了保证区中页的连续性, InnoDB存储引擎一次从磁盘申请4~5个区。在默认情况下, InnoDB 存储引擎页的大小为 16KB,即一个区中一共有 64 个连续的页。
InnoDB中可以使用InnoDB_page_size参数去控制每页的大小,可以设置为4K、8K(默认为16K),这样的话,区中存放的页的数量就会相应的变为256和128,但区的大小总是为1MB。
但是,当用户启用了innodb_file_per_table后,创建的表默认大小是96KB,而一个区至少是1MB,所以创建的表应该至少也为1MB才对,为什么会这样?
其实这是因为,在每个段开始时,会先用32个页大小的碎片页来存放数据,在使用完这些碎片页才会进行区的申请(直接申请4~5个区),这样做的目的在于,对于一些小表,可以在开始时就占用比较少的空间,节省磁盘容量的开销。
页
页是InnoDB磁盘管理的最小单位,默认的页大小为16KB,可以通过innodb_page_size参数进行修改,若修改完成,则所有表中页的大小都会改变,且不可以再次进行修改,除非产生新的库。
常见的页类型有如下:
- 数据页(B-tree Node)
- undo页(undo log page)
- 系统页(system page)
- 事务数据页(transaction system page)
- 插入缓冲位图页(insert buffer bitmap)
- 插入缓冲空闲列表页(insert buffer free list)
- 未压缩的二进制大对象页(uncompressed blob page)
- 压缩的二进制大对象页(compressed blob page)
行
InnoDB存储引擎是面向列(row-oriented)的,也就是说数据是按行来进行存放的(数据表中的一行),而每个页的存放的行记录也是有硬性定义的,最多允许存放7992数量的行记录(计算公式为16 * 1024 /2 - 200,16是指页的大小为16KB)。