咱们知道,要处理数据,必须先把数据放到内存中来,那么Mysql读写记录时,是怎么读写的勒?Mysql是将数据划分为若干个页,以页做为磁盘和内存之间交互的基本单位,InnoDB中页的大小通常为 16 KB。也就是在通常状况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。算法
所谓行格式就是表的一条记录在磁盘里存储的二进制格式。迄今为止,InnoDB有四种行格式,分别是Compact、Redundant、Dynamic和Compressed行格式。下面分别介绍下这几种行格式。sql
示意图:学习
一条记录存储分为记录的额外信息和记录的真实数据两部分:编码
记录的额外信息又包括变长字段长度列表、NULL值列表、记录头信息:code
所谓变长字段,指的是如VARCHAR(M)、VARBINARY(M)、各类TEXT类型,各类BLOB类型的字段,变长字段列表主要是存储的这些字段的真实数据占用的字节长度,该列表的顺序是按表字段逆序。在长度列表中每一个字段用1-2个字节来其字节长度,具体是1仍是2个字节是经过该记录该字段占用的最大字节长度和真实数据长度来计算获得的。具体规则是:先看字段最大字节长度,小于255直接用1个字节表示,那若是大于255的勒?好比utf8编码格式下的varchar(100),最大字节长度是3*100=300,超过了一个字节能表示的最大的数。这时应该看真实数据字符占用字节数,若是真实数据字符占用数据字节数小于127,用1个字节,大于用2个字节。为何是用127作划分勒,由于一个字节有8位,首位被用来标识需不须要一块儿读取下个字节做为字段的字节长度(即这个字段用的是1个字节表示长度仍是2个字节)。0须要,1表示不须要。若是碰到该记录数据字节太长,产生行溢出时(后面会细讲),这种状况的话,变长字段长度列表中表示该字段的长度仍是2个字节,只表示该记录在本页的数据长度。由于2个字节所能表示的字节长度有2的15次方,远远大于InnoDb读写一页(16KB)的长度了,因此就算该记录只有一个字段,本页数据全存该字段的数据,那2个字节来表示本页所占长度也是彻底放得下的。blog
注:对于 CHAR(M) 类型的列来讲,当列采用的是定长字符集时,该列占用的字节数不会被加到变长字段长度列表,而若是采用变长字符集时,该列占用的字节数也会被加到变长字段长度列表。另外定长字符集下的CHAR类型字段,若是涉及更新或删除的话,不会产生硬盘碎片,效率比varchar高。内存
null值列表只有在该记录所在表的元数据规定有字段能够存在null值才会有null值列表,null值列表是基于位向量来维护字段是否为null的,即用二进制位的0和1表示字段是否为null,该列表也是逆序的。另外InnoDB还规定NULL值列表必须用整数个字节的位表示,若是使用的二进制位个数不是整数个字节,则在字节的高位补0。ci
记录头信息由固定5个字节组成包括:io
上图中,有些概念可能不清楚,后面若是再开文章的话再学习。table
记录的真实数据除了用户本身定义的列的数据之外,InnoDB还会为每一个记录默认的添加一些列(也称为隐藏列),具体的列以下:
其中row_id不必定是必须的,只有在表中不存在主键的时候InnoDB才会自动添加这列。
示意图:
这个行格式名称是也就是Redundant,表示它是已是过期了的了,如今通常不用,但这里仍是介绍一下,对比与Compact的区别。
与变长字段长度列表有两处不一样:
从上面看出,字段长度偏移列表实质上是存储每一个列中的值占用的空间在记录的真实数据处结束的位置,这种表示方法相对来讲更简单直观。
注:对于到底用1个字节或2个字节,规则相似Compact,但判断的是该记录全部字段真实数据长度,若是真实数据小于127字节,则每一个列对应的偏移量占用1个字节,大于127,用两个字节来划分,固然真实数据可能超过了2个字节所能表示的最大字节数32767,这时依旧是两个字节,缘由同Compact,2个字节足够表示该页的最大偏移(由于1页就16K,也就是16384个字节),剩下的为溢出列数据,交由其余页存放,本页只存其余页的指向地址。
与Compact的记录头信息相比:
Redundant跟Compact不同的是,把是一个或两个字节表示长度放在了头信息里面,具体规则相似Compact,但判断的是该记录全部字段真实数据长度,若是真实数据小于127字节,则每一个列对应的偏移量占用1个字节,大于127(这里可能会疑问为何是127而不是255(1个字节表示的最大的数),由于与Compact格式不一样,Redundant对null值信息没有集中存储,而是将字段长度偏移列表首个字节利用起来,标识了该字段为不为null),用两个字节来划分,固然真实数据可能超过了2个字节所能表示的最大字节数32767,这时依旧是两个字节,缘由同Compact,2个字节足够表示该页的最大偏移(由于1页就16K,也就是16384个字节),剩下的为溢出列数据,交由其余页存放,本页只存其余页的指向地址。
与Compact的区别是:
不会在记录的真实数据处存储字段真实数据的前768个字节,而是把全部的字节都存储到其余页面中,只在记录的真实数据处存储其余页面的地址。
与Dynamic不一样的一点是:
Compressed行格式会采用压缩算法对页面进行压缩,以节省空间。
在我使用的版本Mysql5.7.26版本中,行格式默认为Dynamic。如何进行查看某个表的行格式命令是:
show table STATUS like '表名'
建立或修改表的语句:
MySQL对一条记录占用的最大存储空间是有限制的,除了BLOB或者TEXT类型的列以外,其余全部的列(不包括隐藏列和记录头信息)占用的字节长度加起来不能超过65535个字节。若是超过会报ERROR,好比建立一个只有一个字段编码格式为ascii(1个字节为1个字符)的表:
CREATE TABLE varchar_size_demo( -> c VARCHAR(65535) -> ) CHARSET=ascii ROW_FORMAT=Compact; 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
注:这里的65535包含除了列自己的数据以外,还包括一些其余的数据(storage overhead)如NULL值标识(能够为null的字段须要这个标识)、真实数据占用字节的长度。这样能够计算一下,若是只有一个字段的表,能够为null,那么该字段真实数据可用字节就是65535-2(真实数据占用字节的长度)-1(null值标识)=65532,以上语句能够改成:
CREATE TABLE varchar_size_demo( c VARCHAR(65532) ) CHARSET=ascii ROW_FORMAT=Redundant > OK > 时间: 0.124s
这样就偏偏够装。
在Compact和Reduntant行格式中,对于占用存储空间很是大的列,在记录的真实数据处只会存储该列的一部分数据,把剩余的数据分散存储在几个其余的页中,而后记录的真实数据处用20个字节存储指向这些页的地址(固然这20个字节中还包括这些分散在其余页面中的数据的占用的字节数),从而能够找到剩余数据所在的页。如图:
那么这里怎么计算,产生行溢出的数据长度的临界点勒?这里与几个限制有关:
即只要保证2条数据,加起来数据大小不超过16K减去页中其余不用于存储记录的大小(固定132个字节),就不会产生行溢出,可是一条数据不止有存储真实数据还有其余。以Compact为例,假设只有1个字段,且能够为Null,则每一个记录须要的额外信息是27字节,包括:
假设一个列中存储的数据字节数为n,只要知足:
132 + 2×(27 + n) < 16384
则该记录不会形成行溢出。固然若是表中有多个字段,上面公式中的27可能会增长(由于“真实数据的长度”和“列是不是NULL值”占用字节可能会增长)。