java八股文面试[数据库]——行溢出

行记录格式
1) 行格式分类

表的行格式决定了它的行是如何物理存储的,这反过来又会影响查询和DML操作的性能。如果在单个page页中容纳更多行,查询和索引查找可以更快地工作,缓冲池中所需的内存更少,写入更新时所需的I/O更少。

InnoDB存储引擎支持四种行格式:Redundant、Compact、Dynamic 和 Compressed .

查询MySQL使用的行格式,默认为: dynamic

mysql> show variables like 'innodb_default_row_format';
+---------------------------+---------+
| Variable_name             | Value   |
+---------------------------+---------+
| innodb_default_row_format | dynamic |
+---------------------------+---------+

指定行格式语法

CREATE TABLE <table_name(column_name)> ROW_FORMAT=行格式名称
ALTER TABLE <table_name> ROW_FORMAT=行格式名称
2) COMPACT 行记录格式

Compact 设计目标是高效地存储数据,一个页中存放的行数据越多,其性能就越高。

Compact行记录由两部分组成: 记录放入额外信息 和 记录的真实数据.

image.png

记录额外信息部分

服务器为了描述一条记录而添加了一些额外信息(元数据信息),这些额外信息分为3类,分别是: 变长字段长度列表、NULL值列表和记录头信息.

  • 变长字段长度列表

    MySQL支持一些变长的数据类型,比如VARCHAR(M)、VARBINARY(M)、各种TEXT类型,各种BLOB类型,这些变长的数据类型占用的存储空间分为两部分:

    1. 真正的数据内容

    2. 占用的字节数

    变长字段的长度是不固定的,所以在存储数据的时候要把这些数据占用的字节数也存起来,读取数据的时候才能根据这个长度列表去读取对应长度的数据。

    Compact行格式中,把所有变长类型的列的长度都存放在记录的开头部位形成一个列表,按照列的顺序逆序存放,这个列表就是 变长字段长度列表

  • NULL值列表

    表中的某些列可能会存储NULL值,如果把这些NULL值都放到记录的真实数据中会比较浪费空间,所以Compact行格式把这些值为NULL的列存储到NULL值列表中。( 如果表中所有列都不允许为 NULL,就不存在NULL值列表 )

  • 记录头信息

    记录头信息是由固定的5个字节组成,5个字节也就是40个二进制位,不同的位代表不同的意思,这些头信息会在后面的一些功能中看到。

    名称 大小(单位:bit) 描述
    预留位1 1 没有使用
    预留位2 1 没有使用
    delete_mask 1 标记该记录是否被删除
    min_rec_mask 1 标记该记录是否是本层B+树的非叶子节点中的最小记录
    n_owned 4 表示当前分组中管理的记录数
    heap_no 13 表示当前记录在记录堆中的位置信息
    record_type 3 表示当前记录的类型: 0 表示普通记录, 1 表示B+树非叶子节点记录, 2 表示最小记录,3表示最大记录
    next_record 16 表示下一条记录的相对位置
  1. delete_mask

    这个属性标记着当前记录是否被删除,占用1个二进制位,值为0 的时候代表记录并没有被删除,为1 的时候代表记录被删除掉了

  2. min_rec_mask

    B+树的每层非叶子节点中的最小记录都会添加该标记。

  3. n_owned

    代表每个分组里,所拥有的记录的数量,一般是分组里主键最大值才有的。

  4. heap_no

    在数据页的User Records中插入的记录是一条一条紧凑的排列的,这种紧凑排列的结构又被称为堆。为了便于管理这个堆,把记录在堆中的相对位置给定一个编号——heap_no。所以heap_no这个属性表示当前记录在本页中的位置。

  5. record_type

    这个属性表示当前记录的类型,一共有4种类型的记录, 0 表示普通用户记录, 1 表示B+树非叶节点记录, 2 表示最小记录, 3 表示最大记录。

  6. next_record

    表示从当前记录的真实数据到下一条记录的真实数据的地址偏移量,可以理解为指向下一条记录地址的指针。值为正数说明下一条记录在当前记录后面,为负数说明下一条记录在当前记录的前面。

  • 记录真实数据部分

    记录的真实数据除了插入的那些列的数据,MySQL会为每个记录默认的添加一些列(也称为隐藏列),具体的列如下:

    image.png

    列名 是否必须 占用空间 描述
    row_id 6字节 行ID,唯一标识一条记录
    transaction_id 6字节 事务ID
    roll_pointer 7字节 回滚指针

    生成隐藏主键列的方式有:

1. 服务器会在内存中维护一个全局变量,每当向某个包含隐藏的row_id列的表中插入一条记录时,就会把该变量的值当作新记录的row_id列的值,并且把该变量自增1。
2. 每当这个变量的值为256的倍数时,就会将该变量的值刷新到系统表空间的页号为7的页面中一个Max Row ID的属性处。
3. 当系统启动时,会将页中的Max Row ID属性加载到内存中,并将该值加上256之后赋值给全局变量,因为在上次关机时该全局变量的值可能大于页中Max Row ID属性值。
3) Compact中的行溢出机制

什么是行溢出 ?

MySQL中是以页为基本单位,进行磁盘与内存之间的数据交互的,我们知道一个页的大小是16KB,16KB = 16384字节.而一个varchar(m) 类型列最多可以存储65532个字节,一些大的数据类型比如TEXT可以存储更多.

如果一个表中存在这样的大字段,那么一个页就无法存储一条完整的记录.这时就会发生行溢出,多出的数据就会存储在另外的溢出页中.

总结: 如果某些字段信息过长,无法存储在B树节点中,这时候会被单独分配空间,此时被称为溢出页,该字段被称为页外列

Compact中的行溢出机制

InnoDB 规定一页至少存储两条记录(B+树特点),如果页中只能存放下一条记录,InnoDB存储引擎会自动将行数据存放到溢出页中. 当发生行溢出时,数据页只保存了前768字节的前缀数据,接着是20个字节的偏移量,指向行溢出页.

image.png

知识来源:马士兵教育