Mysql文件结构
- db.opt:存储mysql默认字符集,和字符校验规则
- t_order.fmt:存储表结构
- t_order.ibd:存储表数据
表空间结构
表空间结构由:段(segment),区(extent),页(page),行(row)组成
行
数据库表中的数据都是按照行结构进行存放的,每条记录根据不同的行格式,有不同的存储结构
页
类型
常见的有数据页,undo日志页,和溢出页
读存
InnoDB的数据读写是按照页为单位的,一个页的空间默认16kb,也就是说能够最多保证16kb的地址连续,每次需要读取一行记录的时候,是将整个页读取到内存中
区
在InnoDB中的数据存储是用B+来存取的,B+树的每一层是用双向链表来存储的,如果是按照页来分配空间的话,链表相邻两个节点之间的页,物理地址不是连续的,这就导致了磁盘搜索的时候出现大量的随机IO,解决方案也简单。
- 解决方案 当索引量大的时候就不按照页来分配空间,而是按照区来分配空间,一个区是默认是1MB的空间,一个区可以存64个页,这样在一个区内的64个页在物理地址上就是连续的,这个时候搜索的时候就可以使用顺序IO了
段
表空间是由各个段组成的,段是由多个区组成的
段的分类
段可以分为数据段,索引段和回滚段
- 索引段:存储的是非叶子节点的数据
- 数据段:存储的是叶子节点的数据
- 回滚段:存储回滚数据,这也是MVCC实现多版本查询的一个重要实现
行格式
四种行格式
- Redundant:古老的非紧凑的行格式
- Compact:紧凑的行格式
- Dynamic:紧凑的行格式
- Compressed:紧凑的行格式
Compact行格式
Compact分为记录额外信息和记录真实数据的两个部分
记录额外信息
记录额外信息中包含
- 变长字段长度列表
- Null值列表
- 记录头信息
变长字段长度列表
这个是用来记录变长字段的长度的,如varchar变长字符串,只有记录了数据长度,我们才知道要在数据中要读取多大的空间的数据
这个长度的计算是根据字节的长度计算的
如1是一个字节十六进制是0x01,123是三个字节十六进制是0x03
这两列是同属于一行数据的时候
在变长字段长度列表中的存储形式就是03 01是逆序存放的
如果是NULL值是不会存放在真实数据部分的,所以在变长字段长度列表中也不需要存放它。
为什么需要逆序存放?
在记录额外信息的三个字段中,NULL值列表和变长字段长度列表都是逆序存放的,这个的话需要提到我们的CPU cache了
假设我们现在有两条compact行数据A,B,其中A数据的记录头信息的指针是指向B数据的记录头信息和真实数据之间,这样向左读就是额外信息,向右读就是真实数据。
当我们从数据库读取数据的时候,会将一定大小如64kb大小的行数据放到内存中,比如我们现在有name,school,number三个数据都是变长字段,它的额外信息就是number,school,name,读取信息的时候从一开始指针指向的位置读取,这样的话我们就会更有机会把name 和额外信息name,一起读到内存中,不用再跑到数据库额外读取一次,这样大大增加了CPU cache的命中率
NULL值列表也是同理
NULL值列表
NULL值列表可以看作是bit列表,也就是二进制列表,由0代表非空,1代表空
如果三个字段name,school,number,分别为NULL,NULL,3,那么再NULL值列表中就会以00000011来存储,这是按照一个字节来存的,一个字节有八位,如果超过8位我们就会按照2个字节来存,高位补0即可。
记录头信息
记录头信息由几个部分来组成,这里讲常见的部分
- delete_mask:记录是否被删除,删除了就为1(软删除)
- next_record:下一条记录的位置
- record_type:表示当前的记录类型
记录真实数据
记录真实数据中除了我们实际的定义字段以外还有三个隐藏字段
- row_id:占用6个字节,如果我们定义了主键或唯一约束,就不会有这个字段,没有的话这个会代替成为索引功能
- trx_id:占用6个字节,事务id
- roll_pointer:占7个字节,指向上一个版本的指针
这里的trx_id和roll_pointer是在MVCC中有用的字段
如果对事务以及MVCC不熟悉的可以阅读我的文章Mysql事务
varchar(n)中n最大的取值可以是多少
Mysql中规定除了TEXT、BOLBS这种大对象除外,其他所有列,不包含隐藏列和记录头信息,加起来最多不能超过65535个字节
varchar(n)中的n是代表的字符数量而不是字节,如果是一般的ascii字符集,一个字符占用一个一个字节
所以在单字段的情况下我们能够测得n的最大取值,并且我们需要为非空不然的话会需要额外的NULL值列表空间,按照最多字节65535来计算,并且存储的字符是ascii字符那么就是65535字节,然后变长字段长度这里要存储2个字节,如果没有超过255就是一个字节,那么我们就最多能存储65535-2=65533个字符,n的最大取值就是65533
溢出页
Mysql中磁盘和内存交换的基本单位是页,一个页的默认空间是16kb,也就是16384字节,一个变长varchar最多可以存储65533字节的数据,这个时候超过的数据就会存放在溢出页中,此时原来的数据页会用20字节来存放存储指向溢出页的地址
这个就是compact处理溢出数据的方法
compressed和Dynamic这两个行格式和compact很类似,主要的区别就在处理溢出数据上,他们会用完全行溢出的方式,在原数据页中不存储一部分数据只用20字节来存储指向溢出页的地址,溢出页记录溢出的完整数据。