具体细节 请去掘金购买《MySQL 是怎样运行的:从根儿上理解 MySQL》
InnoDB页简介
- 1.innodb把数据按照页为基本单位作为与磁盘的交互单位。
- 2.页大小一般为16KB
InnoDB行格式
- 1.记录是以行格式或者记录格式存储在磁盘
- 2.compact,redundant,dynamic和compressed等四只格式
指定行格式的语法
mysql> CREATE TABLE record_format_demo (
-> c1 VARCHAR(10),
-> c2 VARCHAR(10) NOT NULL,
-> c3 CHAR(10),
-> c4 VARCHAR(10)
-> ) CHARSET=ascii ROW_FORMAT=`COMPACT`;
Query OK, 0 rows affected (0.03 sec)
COMPACT行格式
- 1.主要是两部分:记录的额外信息和记录的真实数据。
- 2.额外信息:变长字段长度列表,NULL值列表,记录头信息
- 3.真实数据:各个列的值。
变长字段长度列表
- 1.VARCHAR(M)、VARBINARY(M)、各种TEXT类型,各种BLOB类型.
- 2.对于变长的字段,mysql存储其真实数据内容和占用的字节数。
- 3.Compact行格式中,所有变长字段的占用的字节长度放在开头形成链表,是逆序存放(比如列a,列b,则链表就是ba)
- 4.如果该可变字段允许存储的最大字节数(M×W)超过255字节并且真实存储的字节数(L)超过127字节,则使用2个字节,否则使用1个字节。
- 5.该字节的第一个二进制位作为标志位:如果该字节的第一个位为0,那该字节就是一个单独的字段长度(使用一个字节表示不大于127的二进制的第一个位都为0),如果该字节的第一个位为1,那该字节就是半个字段长度。
- 6.变长字段长度列表中只存储值为 非NULL 的列内容占用的长度,值为 NULL 的列的长度是不储存的
NULL值列表
- 1.把列中的存储NULL的都放入列表中,这样就不需要再真实数据中占有地方
- 2.如果所有列都没有允许存储NULL 则NULL值列表不存在。
- 3.将允许存储NULL的列表采用二进制位表示,0表示该列值不为NULL,1则是NULL。注意是逆序。
- 4.规定了列表表示的二进制必须是整数个字节,如果NULL值列不够8则 高位设置为0补齐。
记录头信息
- 1.由固定的5个字节组成,40个位可以表示40个不同的意思
记录的真实数据
- 1.除了正常的列信息之外,还会包含一些隐藏列
- 2.row_Id(如果不存在主键或者其他的唯一索引,则有该列)
- 3.tracsaction_id(事务id)
- 4.roll_pointer,指向了历史记录,可以帮助回滚和实现MVCC
CHAR(M)列的存储格式
- 1.当char列采用的是定长字符集时候,该列占用的字节数不会被加到变长字段长度列表,否则会加入
- 2.比如采用了ascii就不需要加入,采用UTF-8就需要加入
- 3.变长字符集的CHAR(M)类型的列要求至少占用M个字节,比如我们采用UTF-8编码CHAR(10),那么字节长度可能为10-30,所以我们存入空字符也会占用10个字节
- 4.上述的好处是如果记录更新,可以直接利用该存储空间,无需重新分配。(当然如果如果更新记录超过10个字节还是需要重新开辟)
注意了在Compact行格式下,Mysql的列有变长类型,同时对列的编码字符集也有动态的,只有是定长类型的列并且也采用定长类型的编码列,占用分配空间大小才固定。
Redundant行格式
- 1.主要是两部分:记录的额外信息和记录的真实数据。
- 2.额外信息:字段长度偏移列表,记录头信息
- 3.记录的真是数据。
字段长度偏移列表
- 1.记录所有字段包含隐藏列的长度信息,逆序。
记录头信息
- 1.Redundant行格式多了n_field和1byte_offs_flag这两个属性。
- 2.Redundant行格式没有record_type这个属性。
- 3.1byte_offs_flag:标记字段长度偏移列表中每个列对应的偏移量是使用1字节还是2字节表示的
- 4.1byte_offs_flag的值是怎么选择的:根据该条Redundant行格式记录的真实数据占用的总大小来判断的:当记录的真实数据占用的字节数不大于127(十六进制0x7F,二进制01111111)时,每个列对应的偏移量占用1个字节
,当记录的真实数据占用的字节数大于127,但不大于32767(十六进制0x7FFF,二进制0111111111111111)时,每个列对应的偏移量占用2个字节。
大于32767的情况存放到了溢出页中 - 5.在Redundant行格式下 多余字节存放溢出页,在本页只保留前768个字节和20个字节的溢出页面地址,因此只需要2个字节记录偏移量
Redundant行格式中NULL值的处理
- 1.因为Redundant行格式并没有NULL值列表
- 将列对应的偏移量值的第一个比特位作为是否为NULL的依据,该比特位也可以被称之为NULL比特位
- 3.正是因为首位被做Null比特位了所以才在大于127的时候使用2个字节来记录长度
- 4.如果NULL列采用定长字符集来存储,则采用0x00字节填充,如果是变长则不占用任何存储空间
CHAR(M)列的存储格式
- 1.Compact行格式在CHAR(M)类型的列中存储数据的时候还挺麻烦,分变长字符集和定长字符集的情况
- 2.而在Redundant行格式中十分干脆,不管该列使用的字符集是啥,只要是使用CHAR(M)类型,占用的真实数据空间就是该字符集表示一个字符最多需要的字节数和M的乘积
- 3.因此不会产生碎片
行溢出数据
- 1.除了BLOB或者TEXT类型的列之外,其他所有的列(不包括隐藏列和记录头信息)占用的字节长度加起来不能超过65535个字节
- 2.这个65535个字节除了列本身的数据之外,还包括一些其他的数据(storage overhead),比如说我们为了存储一个VARCHAR(M)类型的列,其实需要占用3部分存储空间:
真实数据
真实数据占用字节的长度
NULL值标识,如果该列有NOT NULL属性则可以没有这部分存储空间 - 3.如果该VARCHAR类型的列没有NOT NULL属性,那最多只能存储65532个字节的数据
- 4.Compact和Reduntant行格式中,对于占用存储空间非常大的列,在记录的真实数据处只会存储该列的一部分数据
- 5.记录的真实数据处用20个字节存储指向这些页的地址,这些也采用链表进行连接。
- 6.不只是 VARCHAR(M) 类型的列,其他的 TEXT、BLOB 类型的列在存储数据非常多的时候也会发生行溢出。
行溢出的临界点
- 1.MySQL中规定一个页中至少存放两行记录
Dynamic(5.7默认)和Compressed行格式
- 1.Dynamic和compact很相似,处理行溢出数据时有点儿分歧,只记录地址,不会记录部分真实数据。
- 2.Compressed行格式和Dynamic不同的一点是Compressed行格式会采用压缩算法对页面进行压缩,以节省空间。