【Mysql】mysql中的记录是如何存储的?
本文是自己学习的一些总结,比较口语化,主要是为了加强记忆,和希望与大家分享。欢迎大佬们批判和指正!
mysql中数据的存储是靠存储引擎来完成的,每当创建一个数据库的时候,就会创建以database为名的目录。里面会有db.opt文件,.frm文件和.ibd文件。其中,db.opt文件是存储数据库的字符集和字符校验规则的。.frm是表结构文件。.ibd是存储数据的文件。
在mysql行记录的存储中,行格式有Redundant、Compact、Dynamic和 Compressed四种。redundant应该是已经被淘汰了的行格式,没什么人用了。而后面三种其实非常相似。那么以Compact为例。
它是包含记录的额外信息和记录的真实数据两部分的。其中记录的额外信息又包括变长字段的长度列表和null列表和记录头信息。
变长字段的长度列表:当列中没有变长的字段时,这时候行格式里面就不会有这个列表。它记录的是变长字段的数据的长度,而且注意的是,它是逆序记录的。如变长字段是name,address,它在里面记录的顺序是address,name。这个原因在记录头信息那里会提一嘴。
null列表:当列中所有字段都不能为null的时候,也没有这个字段存在。null列表中也是逆序记录的,而且它是用二进制bit位来记录的,1表示为null,0表示不为null。不过它是字节来记录的,不够8位的话会补齐高位。
记录头信息包含很多:比如delete_mask ,标识此条数据是否被删除,我们从这里知道,当我们执行删除操作的时候,其实并不会将数据删除,而是将delete_mask 的值置为1。比如next_record,下一条记录的位置,我们知道每一条记录是以链表的形式连接起来的。这样,向左读就是记录的额外数据向右读就是真实数据。
记录真实数据,除了我们定义的列外,还存在三个隐藏字段。
row_id字段:如果我们在定义表的时候,没有指定主键也没有指定唯一约束字段,就会添加row_id字段。占6个字节。
trx_id字段:事务字段,这个字段是必须的。表明是哪个事务生成了这个数据。
roll_pointer字段:上一个版本的指针,必须的,占7个字节。此处需要涉及MVCC。
varchar(n)中的n最大能取多大?我们知道在mysql中规定,除了text和blob之外,任何不能超过65535字节。这个数值是包括记录的额外信息的,就是一整个行格式的值不能超过65535。所以varchar中n的最大数是,65535-其它字段占的字节数。
万一就是超过了,出现了行溢出怎么办?在这种情况下,就需要用到溢出页。compact是将溢出部分的数据存到溢出页,保存20个字节的地址指向溢出页。而Dynanic和Compressed则使用的是完全行溢出,只在记录真实数据那里保存20个字节的溢出页地址,将数据全部保存到溢出页。
总结
MySQL 的 NULL 值是怎么存放的?
MySQL 的 Compact 行格式中会用「NULL值列表」来标记值为 NULL 的列,NULL 值并不会存储在行格式中的真实数据部分。
NULL值列表会占用 1 字节空间,当表中所有字段都定义成 NOT NULL,行格式中就不会有 NULL值列表,这样可节省 1 字节的空间。
MySQL 怎么知道 varchar(n) 实际占用数据的大小?
MySQL 的 Compact 行格式中会用「变长字段长度列表」存储变长字段实际占用的数据大小。
varchar(n) 中 n 最大取值为多少?
一行记录最大能存储 65535 字节的数据,但是这个是包含「变长字段字节数列表所占用的字节数」和「NULL值列表所占用的字节数」。所以, 我们在算 varchar(n) 中 n 最大值时,需要减去这两个列表所占用的字节数。
如果一张表只有一个 varchar(n) 字段,且允许为 NULL,字符集为 ascii。varchar(n) 中 n 最大取值为 65532。
计算公式:65535 - 变长字段字节数列表所占用的字节数 - NULL值列表所占用的字节数 = 65535 - 2 - 1 = 65532。
如果有多个字段的话,要保证所有字段的长度 + 变长字段字节数列表所占用的字节数 + NULL值列表所占用的字节数 <= 65535。
行溢出后,MySQL 是怎么处理的?
如果一个数据页存不了一条记录,InnoDB 存储引擎会自动将溢出的数据存放到「溢出页」中。
Compact 行格式针对行溢出的处理是这样的:当发生行溢出时,在记录的真实数据处只会保存该列的一部分数据,而把剩余的数据放在「溢出页」中,然后真实数据处用 20 字节存储指向溢出页的地址,从而可以找到剩余数据所在的页。
Compressed 和 Dynamic 这两种格式采用完全的行溢出方式,记录的真实数据处不会存储该列的一部分数据,只存储 20 个字节的指针来指向溢出页。而实际的数据都存储在溢出页中。
本专栏是本人学习mysql的一些总结,也欢迎大家评论区留言讨论交流~