目录
4.1 索引组织表
索引组织表:表都是根据主键顺序组织存放的。
每张表都有一个主键,如果创建表时没有显式地定义主键,InnoDB首先会判断是否有非空的唯一索引,有,则该列为主键;否则自动创建一个6字节大小的指针_rowid。
4.2 InnoDB逻辑存储结构
所有数据都被逻辑地存放在一个空间(tablespace)中,称之为表空间。表空间又由段(segment)、区(extent)、页(page)组成。
4.2.1 表空间
所有数据都存放在表空间中,默认ibdata1,如果启用innodb_file_per_table,则每个表内的数据可以单独放到一个表空间(.ibd)内。
注意,如果启用了innodb_file_per_table,每张表的表空间内存放的只是的数据、索引和插入缓冲BITMAP页,其他类的信息,如回滚日志、插入缓冲、索引页、系统事务信息、二次缓冲写等还是存放在原来的共享表空间内。
4.2.2 段
常见的段有数据段、索引段、回滚段等。
InnoDB存储引擎表是索引组织的,因此数据即索引、索引即数据。数据段即为B+树的叶子节点,索引即为B+树的非索引节点。回滚段比较特殊,后面单独介绍。
4.2.3 区
由连续页组成的空间,在任何情况下每个区的大小都为1MB。为了保证区中页的连续性,InnoDB 存储引擎一次从磁盘申请4~5个区。在默认情况下,InnoDB存储引擎页的大小为16KB,即一个区中一共有64个连续的页。
启用innodb_file_per_table后,先使用32个页大小的碎片页来存放数据,在使用完这些页之后才是64个连续页的申请。目的:在开始时申请较小的空间,节约磁盘容量的开销。
4.2.4 页
磁盘管理的最小单位,默认16KB。
常见的页类型有:
■ 数据页
■ undo页
■ 系统页
■ 事务数据页
■ 插入缓冲位图页
■ 插入缓存空闲列表页
■ 未压缩的二进制大对象页
■ 压缩的二进制大对象页
4.2.5 行
InnoDB存储引擎是面向行的,数据是按照行进行存放的。每个页最多存放16KB/2-200=7992
行记录。
4.3 行记录格式
通过SHOW TABLE STATUS like 'table_name'
来查看当前表使用的行格式。
行记录格式有:Compact、Redundant、Compress、Dynamic。
mysql> show table status like 'user'\G;
************************* 1. row ***************************
Name: user
Engine: InnoDB
Version: 10
Row_format: Compact
Rows: 30
Avg_row_length: 546
Data_length: 16384
Max_data_length: 0
Index_length: 16384
Data_free: 0
Auto_increment: 1037
Create_time: 2019-09-11 04:27:05
Update_time: NULL
Check_time: NULL
Collation: utf8_bin
Checksum: NULL
Create_options: row_format=COMPACT
Comment:
1 row in set (0.00 sec)
4.3.1 Compact行记录格式
MySQL 5.0引入,设计目标是高效地存储数据。一个页存放的行数据越多,其性能就越1高。
1. 变成字段长度列表
非NULL,记录字段的实际长度,按照列的顺序逆序放置,若列的长度小于255字节,用1字节表示;若大于255个字节,用2字节表示。变成字段的长度最大不能超过2字节,因为VARCHAR类型的最大长度限制为65535。
2. NULL标志位
标识行数据是否有NULL值,有则用1表示
3. 记录头信息
4. 列数据
实际存储每个列的数据。
注意:NULL不占该部分任何空间,即NULL除了占有NULL标志位,实际存储不占用任何空间。
5. 隐藏列
事务ID列和回滚指针,如果没有定义主键,每行还会增加一个6字节的rowid列。
案例:
1. 新建表并且向表中插入数据
2. 分析结果
4.3.2 行溢出数据
将一条记录中的某些数据存储在真正的数据页面之外。
VARCHAR最多可以存储65535字节,但是真的可以吗?
mysql> create table test(
-> a varchar(65535)
-> )charset=latin1 engine=innodb;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
说明有其他开销(主键、事务id、回滚指针),实际能存放65532。
如果设置字符类型为UFT-8和GBK,结果如何?
行溢出数据的存储,数据页保存前768字节的前缀数据,之后是偏移量,指向行溢出页。
InnoDB 1.0.x版本开始引入了新的文件格式:Compressed和Dynamic,采用完全的行溢出方式,在数据页只存放20个字节的指针,实际存放的数据都在Off Page中。
4.3.4 CHAR的行结果存储
在多字节多字符的情况下,CHAR和VARCHAR的实际行存储基本是没有区别的。
4.4 InnoDB数据页结构
由七个部分组成,如下:
名称 | 字节数 | 作用 |
---|---|---|
File Header(文件头) | 38 | 记录页的一些头信息 |
Page Header(页头) | 56 | 记录数据页的状态信息 |
Infimum和Supremum Record | -- | 虚拟的行记录,用来限定记录的边界 |
User Record(用户记录,即行记录) | -- | 实际存储行记录内容 |
Free Space(空闲空间) | -- | 一条记录被删除后,该空间会被加入到空闲列表中 |
Page Directory(页目录) | -- | 存放记录的相对地址 |
File Trailer(文件结尾信息) | 8 | 检测页是否已经完整地写入磁盘 |
4.4.1 Infimum和Supremum Record
虚拟的行记录,用来限定记录的边界。
Infimum记录是比该页中任何主键值都要小的值,Supremum指比任何可能大的值还要大的值。这两个值在页创建时被建立,并且在任何情况下不会被删除。
4.4.2 Page Directory
存放记录的相对位置(注意,这里存放的是页相对位置,而不是偏移量),有时候这些记录指针称为Slots(槽)或者目录槽(Directory Slots)。一个槽中可能有多个记录。由于Page Directory是稀疏目录,二叉查找的结果只是一个粗略的结果,因此必须通过record header中的next_record来继续查找相关记录。
如下图,15,18组成一个槽,找到槽之后,还需要通过指针找到记录。