MySQL之行格式、页结构

MySQL之行格式、页结构

前言

关于为何要了解MySQL的物理实现:

其实像B+索引,多版本并发控制(MVCC)等MySQL常问的技术知识点都是会对应到具体的物理实现上,如果不了解MySQL到底怎么存储数据,不清楚每个数据行中有什么结构,不清楚B+树中的一个节点对应什么物理结构,又怎么算了解了MySQL

从图开始理解

以下面这段创建代码为例:

mysql> CREATE TABLE format_demo (
    ->      c1 VARCHAR(10),
    ->      c2 VARCHAR NOT NULL,
    ->      c3 CHAR(10),
    ->      c4 VARCHAR(10)
    )

那么现在这个表在我们眼中是这样的:(插入了两条数据)

mysql> SELECT * FROM record_format_demo;
+------+-----+------+------+
| c1   | c2  | c3   | c4   |
+------+-----+------+------+
| aaaa | bbb | cc   | d    |
| eeee | fff | NULL | NULL |
+------+-----+------+------+
2 rows in set (0.00 sec)

mysql>

对应在磁盘中表又是怎么样存储的呢?

行格式

所谓表的构成,实际就是一行行的数据,所以在磁盘中表是按行数据进行存储的。那么在磁盘中是一整个表的数据都连续放在一起么?显然不可能,思考数据分页的方法,MySQL也是按照分页的方式将一个表的数据拆分开存放。以InnoDB来说:

  • 将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为 16 KB。也就是在一般情况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。

关于的概念先放到这之后再说。

所以现在我们前进了一大步,起码知道磁盘里的表大概长这样:

磁盘中按页存储的数据.png

再回到行格式上,MySQL中涉及的有这4种:

  1. COMPACT行格式

  2. Redundant行格式

  3. Dynamic行格式

  4. Compressed行格式

主要介绍最重要的COMPACT格式,首先它长这样:

COMPACT行格式.png

可以看到MySQL除了记录用户提供的信息之外还记录了相当的额外信息,这些信息可以分为3类:

  1. 变长字段长度列表

  2. NULL值列表

  3. 记录头信息

简要说明这些额外信息

变长字段长度列表真实数据中的每个非空列按其数据字节长度逆序存放起来

变长字段.png

空间分配:长度不是固定的,取决于有多少数据

NULL值列表中储存了所有没有设置NOT NULL的字段(列),并用一个2进制位表示该列的NULL状态,同时这个列表也是按列顺序的倒序排列的,也就是如果该行数据中对应第1,3,4个字段可以为空(没有设置NOT NULL)那么NULL值列表就长这样:

NULL列表.png

需要说明的是,MySQL中无论是定长数据还是非定长数据都可以设置对NULL的控制,所以如果该列不为空那么该列数据的相关信息就存在变长字段长度列表中。

空间分配:同时NULL值列表是按整数倍字节分配空间的,不足的位置补上0.

记录头信息存储的都是和行数据控制相关的内容:

image.png

空间分配:固定5个字节40个二进制位

每段内容记录信息如下:(不做详细解释了,了解大概即可)

名称 大小(Bit) 描述
预留位 1 -
预留位 1 -
delete_mash 1 删除标记位
min_rec_mask 1 B+树的每层非叶子节点中的最小记录都会添加该标记
n_owned 4 表示当前记录拥有的记录数
heap_no 13 表示当前记录在记录堆的位置信息
record_type 3 表示当前记录的类型,0表示普通记录,1表示B+树非叶子节点记录,2表示最小记录,3表示最大记录
next_record 16 表示下一条记录的相对位置

记录的真实数据

说完记录中的额外信息,那么记录里的真实数据真的只有用户定义的数据么?

显然不是,MySQL会自动为每个行数据增加一些额外的列,例如DB_ROW_ID,DB_TRX_ID, DB_ROW_PTR分别表示数据行的行id, 行的事务id指向下一个版本数据行的指针,其中事务id指向下一版本的指针实际都是关系到MySQL多版本并发控制的具体实现。

所以到目前为止,我们已经完整了解了一条数据行到底长什么样:

单个数据行.png

关于定长和非定长类型

VARCHAR()和CHAR()类型的大致区别,在根据建表时设定的不同字符集下(字符在字符集中对应占字节L),传入类型中的Maxlen会限定给CHAR(Maxlen)至少分配ML的空间,而VARCHAR(Maxlen)则是完全根据数据具体字符个数*来计算分配。计算分配的规则不在这里说明。

页结构

下面是页结构的示意图:


页结构.png

可以在中间找到我们刚刚讨论的行数据位于User Records中。

上述内容简述功能如下:

名称 中文名 占用空间 描述
File Header 文件头部 38字节 页的一些通用信息
Page Header 页面头部 56字节 数据页专有的一些信息
Infimum + Supremum 最小记录和最大记录 26字节 两个虚拟的行记录
User Records 用户记录 不确定 实际存储的行记录内容
Free Space 空闲空间 不确定 页中尚未使用的空间
Page Directory 页面目录 不确定 页中的某些记录的相对位置
File Trailer 文件尾部 8字节 校验页是否完整

其中用户记录开始是没有的,当插入行数据时会使用空闲空间,当空闲空间装满之后就要申请新的

插入行数据过程.png

继续说明行记录在页中怎么组织又需要使用到之前提到的行中的头信息

大致如下:

  • heap_no属性表示当前记录在中的位置,用户插入的数据一般从2开始排序,为什么是2呢?

    因为 0,1中固有的最大最小行记录,分别指向最大和最小数据。这里其实涉及到了MySQLB+树的物理实现过程,简单来说就是其实就是B+树的一个物理节点,而B+树中所有节点是顺序排列的,这个排列的顺序就是按照表的唯一主键来进行的(一般会设置一个与业务无关的逻辑主键,并且自增)。所以这两个多余的记录就可以理解成节点链表中的头尾节点,用于快速遍历节点。

  • next_record记录了从当前记录到下一行数据的地址偏移量

那么现在中的行数据就是这样串起来的:

页中的行数据.png

这里再提供一个简图说明一个页是B+树中的一个节点这句话的意义:

页和B+树.png

页目录

所以B+树中的索引是怎么在页中实现的呢?这里就涉及到页中另外一个关键的内容:页目录

页目录就是MySQL在页中可以快速检索数据的保障。如果没有页目录,那么定位到了一个不就只能顺序遍历一次链表了。当然,页目录得以实现的基础也是行数据是按主键进行排序存放的。

而目录的制作过程大致是这样的:

  1. 将所有正常的记录(包括最大和最小记录,不包括标记为已删除的记录)划分为几个组。

  2. 每个组的最后一条记录(也就是组内最大的那条记录)的头信息中的n_owned属性表示该记录拥有多少条记录,也就是该组内共有几条记录。

  3. 将每个组的最后一条记录的地址偏移量单独提取出来按顺序存储到靠近的尾部的地方,这个地方就是所谓的Page Directory,也就是页目录(此时应该返回头看看页面各个部分的图)。页面目录中的这些地址偏移量被称为(英文名:Slot),所以这个页面目录就是由组成的。

那么最小只有一个分组的概况如下:


一个分组.png

可以观察到最小记录的n_owned值为1,而最大记录的n_owned值为5。这关系到每个分组是如何设计的:

对于最小记录所在的分组只能有 1 条记录,最大记录所在的分组拥有的记录条数只能在 1~8 条之间,剩下的分组中记录的条数范围只能在是 4~8 条之间。所以分组是按照下边的步骤进行的:

  • 初始情况下一个数据页里只有最小记录和最大记录两条记录,它们分属于两个分组。

  • 之后每插入一条记录,都会从页目录中找到主键值比本记录的主键值大并且差值最小的槽,然后把该槽对应的记录的n_owned值加1,表示本组内又添加了一条记录,直到该组中的记录数等于8个。

  • 在一个组中的记录数等于8个后再插入一条记录时,会将组中的记录拆分成两个组,一个组中4条记录,另一个5条记录。这个过程会在页目录中新增一个来记录这个新增分组中最大的那条记录的偏移量。

所以最终一个有多个分组的页面结构就长这样:

多个分组的页.png

File Header和File Trailer

不出意料Header中保存了这个中的一些信息,而File Trailer中保存了指向下一个页的指针,用于串联页面,也就是B+树。

全文内容总结自:掘金小册《MySQL是怎样运行的》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一. Java基础部分.................................................
    wy_sure阅读 3,846评论 0 11
  • 1. 字符集和比较规则 MySQL有4个级别的字符集和比较规则,分别是: 服务器级别 数据库级别 表级别 列级别 ...
    aiwen2017阅读 618评论 0 0
  • Mysql--InnoDB数据页结构 页 1.页是innodb管理存储空间的基本单位 2.一般大小是16kb 3....
    简书徐小耳阅读 2,478评论 1 1
  • 什么是数据库? 数据库是存储数据的集合的单独的应用程序。每个数据库具有一个或多个不同的API,用于创建,访问,管理...
    chen_000阅读 4,065评论 0 19
  • 很久没画这样的图,生疏了,,,
    遥遥在远方阅读 275评论 1 5