MySQL 对一条记录占用的最大储存空间是有限制的,除了 BLOB 和 TEXT 类型之外,其他所有列 (不包括隐藏列和记录头信息) 占用的字节长度不能超过 65535 个字节,当记录长度超过限制时,MySQL 会建议使用 TEXT 或 BLOB 类型。
innodb_page_size,这个的默认值是16K,每个page两行数据,所以每行最大8k数据。
InnoDB Buffer Pool管理页面本身也有代价,Page数越多,那么相同大小下,管理链表就越长。因此当我们的数据行本身就比较长(大块插入),更大的页面更有利于提升速度,因为一个页面可以放入更多的行,每个IO写的大小更大,可以更少的IOPS写更多的数据。 当行长超过8K的时候,如果是16K的页面,就会强制转换一些字符串类型为TEXT,把字符串主体转移到扩展页中,会导致读取列需要多一个IO,更大的页面也就支持了更大的行长,64K页面可以支持近似32K的行长而不用使用扩展页。 但是如果是短小行长的随机读取和写入,则不适合使用这么大的页面,这会导致IO效率下降,大IO只能读取到小部分。
储存数据
储存上限 65535 个字节不仅包含本身的数据,还包含一些其他数据 (storage overhead),以 VERCHAR 类型为例,共需要 3 部分储存空间:
- 真实数据
- 真实数据占用的字节长度
- NULL 值标识,NOT NULL 列没有这部分
扩展
- 当使用 ascii 字符集时:如果 '真实数据占用的字节长度' 占用两个字节,NULL 值标识占用一个字节,则真实数据最多只能存储 65532 个字符
- 当使用 utf8 字符集时:如果 '真实数据占用的字节长度' 占用两个字节,NULL 值标识占用一个字节,则真实数据最多只能存储 21844 (65532 / 3) 个字符
行溢出
由于 MySQL 中以页为基本单位来管理储存空间的,所有的记录都会被分配到页中。
由于一个页一般为 16KB (16384 个字节),而一个 VERCHAR 最多可以存储 65532 个字节,所以会出现一个页存放不下一条记录的情况,造成行溢出。
不仅是 VERCHAR,BLOB 和 TEXT 也会发生行溢出。
存储方式
对于不同行格式,有不同储存溢出页的方式。
①compact
如果blob列值长度 <= 768 bytes,不会发生行溢出(page overflow),内容都在数据页(B-tree Node);如果列值长度 > 768字节,那么前768字节依然在数据页,而剩余的则放在溢出页(off-page),如下图:
②compressed或dynamic
对blob采用完全行溢出,即聚集索引记录(数据页)只保留20字节的指针,指向真实存放它的溢出段地址:
行信息统计
select
concat(TABLE_SCHEMA,'.',TABLE_NAME),
ROW_FORMAT,
TABLE_ROWS,
DATA_LENGTH,
AVG_ROW_LENGTH
from information_schema.tables
where
AVG_ROW_LENGTH > 8000
and table_rows > 1000
order by AVG_ROW_LENGTH ;
列信息统计
select
TABLE_SCHEMA,
TABLE_NAME,
COLUMN_TYPE
from COLUMNS
where
CHARACTER_MAXIMUM_LENGTH>768
and TABLE_SCHEMA!='information_schema'
and TABLE_SCHEMA!='mysql'
and TABLE_SCHEMA!='performance_schema'
group by TABLE_SCHEMA,TABLE_NAME ;
参考: