InnoDB是一个将数据存储到磁盘上的存储引擎,所以就算我们关闭、重启服务器,数据还是存在的。而在真正处理数据的时候是在内存中进行的,所以需要把磁盘中的内容加载到内存中。
我们知道读写磁盘是很慢的。当我们想从表里获取数据的时候,InnoDB会一条一条的从磁盘中读出来吗?不会的!因为那样太慢了。它采取的方式是:将数据划分为若干页,以页做为磁盘和内存交互的基本单位。InnoDB中页的大小一般为16KB。
在服务器运行的过程中不可以修改页的大小,只能在初始化数据目录的时候指定。
行格式(row_format):一条数据记录在磁盘上的存储结构。
InnoDB 提供了 4 种行格式,分别是 Redundant、Compact、Dynamic和 Compressed 行格式。
我们可以在创建表或者修改表的语句中指定所使用的行格式
create table 'table info ..' row_format = '行格式名称'
alter table 'table name' row_format = '行格式名称'
Redundant 行格式因为现在基本没人用了,重点介绍 Compact 行格式,因为 Dynamic 和 Compressed 这两个行格式跟 Compact 非常像。
话不多说,直接看图
这部分信息是服务器为了更好的管理记录而不得不额外添加的一些信息,这些额外信息分为三个部分,分别是:变长字段长度列表、NULL值列表和记录头信息。
在mysql中有一些变长的数据类型,比如varchar( )、varbinary( )、text类型、blob类型,我们把使用这个变长类型的列成为变长字段。
所以,在存储数据的时候,也要把数据占用的大小存起来,存到「变长字段长度列表」里面,读取数据的时候才能根据这个「变长字段长度列表」去读取对应长度的数据。
这些变长字段的真实数据占用的字节数会按照列的顺序逆序存放(后面会说为什么要这么设计)。
为了展示具体是怎么保存「变长字段的真实数据占用的字节数」,我们先创建这样一张表,字符集是 ascii(所以每一个字符占用的 1 字节),行格式是 Compact,student 表中 name 和 dream_school 字段是变长字段:
CREATE TABLE `student` (
`id` int(11) NOT NULL,
`name` VARCHAR(20) DEFAULT NULL,
`dream_school` VARCHAR(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB DEFAULT CHARACTER SET = ascii ROW_FORMAT = COMPACT;
我们插入三条记录
我们看看这三条记录的行格式中的 变长字段长度列表 是怎样存储的。
先来看第一条记录:
再来看第二条
第三条记录
第三条记录 dream_school 列的值是 NULL,NULL 是不会存放在行格式中记录的真实数据部分里的。
处理过程:
先来看第一条记录
第一条记录所有列都有值,不存在 NULL 值,用二进制来表示是这样的:
第二条记录
接下来看第二条记录,第二条记录 age 列是 NULL 值,用二进制来表示是这样的:
第三条记录
第三条记录 dream_school 列 和 age 列是 NULL 值,用二进制来表示是这样的:
记录头信息由固定5个字节组成,用于描述记录的一些属性,这里的属性比较多,我们就列举几个相对来说重要点的。
记录真实数据除了记录我们自定义的列的数据外,Mysql还会为每个记录默认添加一些列(隐藏列)
MVCC机制就是依赖 trx_id 和 roll_pointer 来实现的。
MySQL 中磁盘和内存交互的基本单位是页,一个页的大小一般是 16KB,也就是 16384字节,而一个 varchar(n) 类型的列最多可以存储 65532字节,一些大对象如 TEXT、BLOB 可能存储更多的数据,这时一个页可能就存不了一条记录。这个时候就会发生行溢出,多的数据就会存到另外的「溢出页」中。
如果一个数据页存不了一条记录,InnoDB 存储引擎会自动将溢出的数据存放到「溢出页」中。在一般情况下,InnoDB 的数据都是存放在 「数据页」中。但是当发生行溢出时,溢出的数据会存放到「溢出页」中。
当发生行溢出时,在记录的真实数据处只会保存该列的一部分数据,而把剩余的数据放在「溢出页」中,然后真实数据处用 20 字节存储指向溢出页的地址,从而可以找到剩余数据所在的页。大致如下图所示。
MySQL 的 NULL 值是怎么存放的?
MySQL 的 Compact 行格式中会用「NULL值列表」来标记值为 NULL 的列,NULL 值并不会存储在行格式中的真实数据部分。
NULL值列表会占用 1 字节空间,当表中所有字段都定义成 NOT NULL,行格式中就不会有 NULL值列表,这样可节省 1 字节的空间。
行溢出后,MySQL 是怎么处理的?
如果一个数据页存不了一条记录,InnoDB 存储引擎会自动将溢出的数据存放到「溢出页」中。
Compact 行格式针对行溢出的处理是这样的:当发生行溢出时,在记录的真实数据处只会保存该列的一部分数据,而把剩余的数据放在「溢出页」中,然后真实数据处用 20 字节存储指向溢出页的地址,从而可以找到剩余数据所在的页。