ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

InnoDB的逻辑存储结构(表、段、区、页、行)

InnoDB的逻辑存储结构(表、段、区、页、行)

目录

索引组织表

 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参数进行修改,若修改完成,则所有表中页的大小都会改变,且不可以再次进行修改,除非产生新的库。

常见的页类型有如下:

  1. 数据页(B-tree Node)
  2. undo页(undo log page)
  3. 系统页(system page)
  4. 事务数据页(transaction system page)
  5. 插入缓冲位图页(insert buffer bitmap)
  6. 插入缓冲空闲列表页(insert buffer free list)
  7. 未压缩的二进制大对象页(uncompressed blob page)
  8. 压缩的二进制大对象页(compressed blob page)

InnoDB存储引擎是面向列(row-oriented)的,也就是说数据是按行来进行存放的(数据表中的一行),而每个页的存放的行记录也是有硬性定义的,最多允许存放7992数量的行记录(计算公式为16 * 1024 /2 - 200,16是指页的大小为16KB)。

返回列表