前提知识-字符集
- ascii:每个字符一个字节,只能存空格、标点符号、数字、大小写字母和一些不可见字符,不能存汉字
- gbk:每个字符 1-2个字节
- utf-8:每个字符1-3个字节
compact 行格式
解释说明
-
变长字段长度列表:针对 VARCHAR(M)、VARBINARY(M)、各种TEXT类型,各种BLOB类型的数据,记录实际占用的字节数(为null的字段不存储)。逆序存放
-
NULL值列表:记录允许存储null的列(主键列、被NOT NULL修饰的列都是不可以存储NULL值的除外),用二进制位表示该列是否为null。 同样也是逆序存放 但是整数字节存储,不足则填充
-
记录头信息:固定5个字节(40位)
· 预留位1:1b
· 预留位2:1b
· delete_mask: 1b 删除标志。删除后只是打一个标识,所有的删除记录会组成一个删除链表。避免移除之后重新排序消耗性能。删除的空间可以被重用
· min_rec_mask:1b B+树的每层非叶子节点中的最小记录都会添加该标记
· n_owned:
· heap_no:13b 这个属性表示当前记录在本页中的位置。其中,每页都会新增两个虚拟记录,最大和最小
· record_type:3b 0表示普通记录,1表示B+树非叶节点记录,2表示最小记录,3表示最大记录
· next_record:16b 下一个记录的偏移量 -
记录真实的数据
- 隐藏列-rowId 非必须 无主键的是 系统自动生成
- 隐藏列-transaction_id 事务id
- 隐藏列-roll_pointer 回滚指针 mvcc
- 其他真实字段数据
备注:char类型,虽说是固定长度类型,但如果字符集为gkb 或者utf8的时候,实际存储的字节也是无法确定的,因此也会在变长字段长度列表中。和varchar的区别是:CHAR(M)类型的列要求至少占用M个字节(哪怕存一个字符a),而VARCHAR(M)却没有这个要求
Redundant行格式
暂不关注
VARCHAR(M)最多能存储的数据
mysql 一条记录最大的存储空间(不包括隐藏列和记录头信息) <=65535
即:加入只存储一个varchar字段,至少有3部分
- 真实数据
- 真实数据占用字节的长度-最大2字节
- NULL标识(如果有的话)最大1个字节
so 若字符集选择ascii,那最大长度为65532,如果设置了not null 那最大长度为65533。
同理,
如果是gbk则 65532/2
如果是utf-8,则 65532/3
问题来了: innodb 中一页只有16kb,即16384个字节。这样可能存在一页无法存储一行数据的情况。
如果某一列中的数据非常多的话,在本记录的真实数据处只会存储该列的前768个字节的数据和一个指向其他页的地址,然后把剩下的数据存放到其他页中,这个过程也叫做行溢出