MySQL行格式的实现
作者:HGW689
首先我们知道在MySQL中页是数据读写的最小单元,默认是16KB。页内的记录会组成一个单链表,每条记录就是一行数据,行格式决定了一行数据是如何进行物理存储的,进而影响查询和DML操作的性能。
✅ 四种行格式
在InnoDB中,常见的行格式有以下4种:
- compact(紧凑)除了保存字段值外,还会记录头信息和记录变长字段长度列表,以及利用空值列表保存null值。适合处理大量包含可变长度列的数据,如:varchar、varbinarg、blob和text。(对于可变长序列,在真实数据处只会存储该列的前768字节的数据,超出的数据分散存储在其他几个页中。然后在真实数据处用20个字节存储指向溢出页的地址,从而可以找到剩余数据所在的页。这也就是我们常说的“行溢出”)
- redundant(冗余)是MySQL5.0版本之前InnoDB的行记录存储方式,用的比较少。它会将该条记录中所有列(包括隐藏列)的长度信息都存储到“字段长度偏移列表”中。
dynamic(动态)是MySQL7.5版本引入,是compact格式的改进版,其结构与compact格式大致相同。它与compact格式的不同主要在于行溢出的处理,在真实数据处不再去额外记录一部分数据了,而是用20个字节存储指向溢出页的地址,所有的数据全部在溢出页中。这种设计减少了行中额外的开销,提高了存储效率和查询性能。
compressed(压缩)是MySQL5.1中InnoDB的新特性之一,它在dynamic的基础上面进行压缩处理,特别是对溢出页的压缩处理。在查询时,会自动解压数据并返回。但compressed格式其实也是用时间换空间,性能并不友好,不推荐在常见的业务中使用。
其中,MySQL 5.6 之前默认使用 Compact,MySQL 5.7 默认使用Dynamic,而redundant 是比较老的数据格式,compressed 不能应用在系统数据,所以Compact和Dynamic应用较广泛;
✅ 如何指定行格式?
我们可以在创建表的时候指定行格式,或者在表创建之后通过 alter 命令更改表的行格式。
// 创建表指定行格式 CREATE TABLE 表名( 建表语句; ) row_format = 行格式名称; // 修改表的行格式 alter table 表名 row_format = 行格式名称;
✅ 详细谈谈compact行格式
如上图,compact行格式分为四段,分别是:变长字段长度列表+NULL值列表+记录头信息+列值。
变长字段长度列表
char和varchar的区别就是定长和变长,对于变长字段实际存储的数据的长度是不固定的,所以在存储数据的时候也要将数据占用的大小存储起来。也就是,变长列的实际占用字节数以逆序方式存储在变长字段长度列表中。
为什么要逆序存放?
那是因为记录头信息指向下一个记录的指针,指向的是下一条记录的记录头和真实信息之间的位置,这样使得位置靠前的记录真实数据 和 对应的字段长度信息可以同时在一个 CPU缓存中,这样就可以提高CPU缓存命中率
NULL值列表
表中的某些列可能会存储NULL值,如果把这些NULL值都放在记录的真实数据中会比较浪费空间,所以compact行格式把这些值为NULL的列存储到NULL值列表中。如果存在允许 null 值的列,则每个列对应一个二进制位(bit),值为1则代表NULL,0则非空,二进制位按照列的顺序逆序排序列。另外,NULL值列表必须用整个字节的位表示(1个字节8位),如果二进制位个数不足整数个字节,则高位补0。
记录头信息
- 预留位1【1bit】,没有使用;
- 预留位2【1bit】,没有使用;
- delete_mask【1bit】,标记该条记录是否被删除。我们执行delete删除记录时,记录并不会立刻被物理删除,而是将这条记录的delete_mask置为1;
- min_rec_mask【1bit】,标识该条记录是否是某个非叶子节点页内的最小记录,帮助 InnoDB 管理索引页中的记录顺序和边界;
- n_owned【4bit】,记录组内记录数。在页内为了快速二分查找,会分组,只有组内最大记录此字段有值,记录组内记录数;
- heap_on【13bit】,用于标识记录在页内的逻辑位置。页内的记录会组成一个单链表,heap_on用于标识记录在链表中的位置。其中0和1,都是虚拟记录,表示页的最小和最大边界,实际记录位置从2开始递增;
- record_type【3bit】,表示当前记录的类型;
- 0 表示普通记录
- 1 B+树非叶子节点记录
- 2 最小记录
- 3 最大记录
- next_record【16bit】,表示下一条记录的相对位置。
真实数据部分
记录真实数据部分,但需要注意的是~除了我们定义的字段外,还有三个隐藏字段,分别是row_id、trx_id、roll_pointer。
- row_id【6Byte】:如果表中没有指定主键且没有唯一约束列,InnoDB会为记录添加row_id作为主键。如果表中有指定标识或者有唯一约束列,那么不会有row_id隐藏列;
- trx_id【6Byte】:该字段是必须有的。事务id,表示这个数据是由哪个事务生成的;
- roll_pointer【7Byte】:该字段也是必须有的。回滚指针,指向一条undo日志记录。
到此这篇关于MySQL行格式的实现的文章就介绍到这了,更多相关MySQL行格式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!