MySQL的最深处-磁盘文件结构

peng_hui
发布于 2022-9-15 11:48
浏览
0收藏

作者 | 冯凯
来源 | 凯哥的Java技术活(ID:sugelakai)

引言

对于Mysql,我们再熟悉不过了,写SQL语句,创建索引,优化查询。

可能更深一点,我们知道了索引存储结构是B+树。

但是,我们从来没见过,这个B+树到底在哪了?我执行了一条创建索引的语句,它到底创建在哪了?

本文就为你揭开MySql磁盘存储的格式,带你看看索引到底长什么样!

注:本文只针对Innodb引擎。

数据文件存储在哪里?

Linux:/var/lib/mysql/xxxx.ibdWindows:请自己发现!

space

在Mysql中,数据的存储,使用space来进行管理。

什么是space?

我们知道innodb 默认 会开始file per table选项,也就是说你每创建一个表,就会生成对应的.ibd文件,可以简单理解为.ibd文件就是表数据。

space对应了具体的 .ibd文件,可能只对应一个,也可能包含多个ibd文件。不妨先认为一个space就是一个.ibd文件。

page

如果说space是Mysql中最大的单位,那么page就是Mysql中最小的单位了。

page默认单位是16kb,但是可以通过具体的参数进行调整。卖个关子,去Mysql官方文档自行查找如何调整page大小。

一个page的通用结构如下:MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

FIL Header:page的文件头

FIL Tralier:page的文件尾Other Data:根据page的类型,数据的内容也不一样

那么page有哪些类型呢?

最熟悉的当然是索引页,也叫 index page,请记住这个页类型,因为它就是实际存储我们的索引数据的,一页16kb,貌似能存储不少的记录。

另外还有

  • FSP Header/XDES page
  • IBUF_BITMAP page
  • INODE page

暂时我们还用不到,暂时先不用管这三个page类型

extent

一个page只有16kb,很具体,但是太小,这时候Mysql引入了一个extent的概念,也就是区。

1 extent = 64 pages

一个区由连续的64个page组成,大小为 64 x 16kb = 1 mb

区的结构是这样子的:MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

与其说是区的结构,倒不如说是page的集合。毕竟区是由page组成的,能有什么结构呢?

对应着一个区,引入另一个概念,XDES Entry。

什么是XDES Entry?extent describe entey. 也就是对一个区结构的描述。这不是打脸么?上一秒还说区没有结构,是由page组成的,现在又说有一个什么XDES Entry来描述区结构。

我们试想:extent作为64个page的大哥,肯定是要知道page的具体信息的。比如哪个page的数据存满了,哪个page是空的?

这就是XDES Entry负责的内容,它用来记录extent内page的信息。也可以当做是extent的结构吧。

除此之外,XDES Entry还和segment有关系,这点我们稍后再讲。

一个XDES Entry的结构如下图所示:MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

File Segment ID:该区属于哪个file segment。暂时忽略,后面会讲到这是什么。

List Node:因为extent不只一个,那肯定会有很多XDES Entry,List Node就是连接XDES Entry链表的指针。里面有 pre 和 next。

State:状态,表明该extent内的page是否已经存储满了,什么意思?一个 extent有64个page,如果该extent所有的page都是空闲的,那么就是FREE,如果部分空闲,就是FREE_FRAG,如果满了,就是FULL_FRAG。

如果是属于某个segment,那么就是FSEG,表明该区属于某个segment,关于segment 后面会描述。

Page State bitmap:区内的哪个page是空闲的。

可能考虑成如下这样,#代表page已经有数据,.代表空闲,那么 Page state bitmap就好像这样:

###....###...#####.######

FSP HEADER/XDES page

在上一节以及上上一节,我们讲了page,讲了extent,讲了extent的管理单元XDES Entry。

其中page里面我们提到有一种page类型是FSP HEADER/XDES page。

没错,XDES Entry就存在于这种类型的page中。

为什么要叫FSP HEADER/XDES page呢?看着是两种类型的page。其实确实是两种page,但是基本上都是一样的,所有统一称为FSP HEADER/XDES page。

这种类型的page的结构如下所示:MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

对比一下最开始的 page 的结构,必不可少的是FIL Header和FIL Tralier。

然后剩下的就是XDES Entry。值得注意的是在该page还有一个FSP Header,在普通的XDES page中该FSP Header没有任何值。这就是FSP HEADER和XDES page的唯一区别。

那么确切来说,区别是什么呢?在一个space内,该FSP HEADER page只存在一个,位于空间第一页。存储关于整个空间的信息。除此之外,其他的都是XDES page。

每个XDES page一共有256个XDES Entry,我们来做个计算:

1个XDES Entry 描述一个 extent,也就是1M,256个也就是256M,

也就是说一个 XDES page可以管理256M的空间。

看到这里,估计大家已经晕了。莫慌,我来小结一下。

如果把上面的space extent page串起来,就像下面这样:MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

space是最大的,包含了extent,在extent内有64个page。其中第一个page是FSP Header page,描述了该空间的信息。其他的IBUF_BITMAP先不用关心。

INODE

我们已经介绍了 space extent 和 page。

这些都是物理上真是存在的东西,你看我们都是通过大小来描述的,一个page是64k,一个extent是1M。

其实还有一种单位,那就是segment。segment称为段,一种逻辑上的单位。

有点抽象了!segment是什么?它在哪里?它的大小有多大?

我们说到,segment是一个逻辑上的单位,他只是理论上存在的。怎么说?试想你现在有一个数组

int[] array = new int[0,1,2,3,4,5];

整个数组你想象成一个space,想象0和1是一个extent,0,1,2等这些是page。

segment可以想象成指向某几个extent的指针。可以认为多个extent组成了segment。

注意,segment又称为 file segment, 简称FSEG。

就像XDES Entry是extent的管理单元一样,INODE 就是segment的管理单元。

INODE的结构如下图所示:MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

FSEG ID:该INODE 描述的 segment 的 id

segment由多个extent和32个空闲的page组成。之前不是说segment是由extent组成的吗?怎么这会又说还有32个空闲的page?这其实是Mysql的一种优化措施,如果一个segment上去就分配一个64page的extent,可能太过于浪费,或者还有其他的碎片的page没有利用,那么Mysql就是先分配给segment 碎片page,等到塞满32个空闲的page后,才会分配整个extent。

segment内的三个extent集合:

FREE:segment下extent内全都是空闲的page的集合

FULL:segment下extent内全都是用过的page的集合

NOT_FULL:segment下extent部分空闲的page的集合

List Base Node for FREE list:两个指针,指向FREE 集合的头和尾

List Base Node for FULL list:两个指针,指向FULL 集合的头和尾List Base Node for NOT_FULL list:两个指针,指向NOT_FULL 集合的头和尾

Fragment Array:最开始为segment分配空间时,不会一下分配一个extent,那样64页太多了,会先分配一个page,直到分配了32个page后,如果还要分配,那么就一次分配一个extent,到达一定次数后,最后每四个四个分配。

介绍完了segment是逻辑上的空间,以及它的INODE的结构,那么INODE存在哪呢?

细心的读者可能会发现,在上面的关于space extent page的小结的图中,第三个page,它的名字叫INODE。

没错,INODE就存在INODE page中。

INODE page的结构如下:MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

可以看到,一个page中有85个 INODE,能够表示85个segment。

INODE page 位于space的第三个位置,如果一个INODE page不够,就会继续分配新的INODE page。

ok,再来小结一下。

我们现在知道什么?

page/extent/segment/space

大概知道一些他们的关系,但是貌似还是不是很明白,下面我们来详细的绘制下page extent segment space的关系!MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

space:最大!space下面分为很多extent

每个extent下有64个page组成。第一个page是FSP HEADER page,存储了space的信息,以及256个XDES Entry,管理接下来的256个extent。第三个page是INODE page,INODE page中有85个INODE,每个INODE 中有指向分配给该segment的extent的指针。这样INODE就知道了自己对应segment所拥有的extent,也就是page的集合。

index page

介绍完Mysql的文件格式组成,再次强调一下,Mysql存储是用page来存储的。

接下来就是我们最关心的一种page类型了:index page,也就是索引页。

一个Space内大部分都是索引页,也就是我们的数据存储。研究索引页的结构以及存储对理解Mysql的查询优化就显得特别重要。

在Mysql中,索引页分为两种,叶子页(leaf page)/非叶子页(non-leaf page/internal page)。

在非叶子页中,分为中间页和根页(root page),所有的mysql查询都是从root page开始进行B+树的遍历!

Mysql每建立一个索引,就会为其分配两个segment,一个是internal segment,一个是leaf segment。见名思意internal segment存储的是非叶子索引页,leaf segment存储的是叶子索引页。

那么索引页的结构是什么呢?

用图来表示,就是如下:MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

FIL Header:这个应该不用解释了,每个page都有一个FIL Header。但是与其他page的FIL Header不同的是,每个index page都有一个指向上一个和下一个index page的指针。

FSEG Header:只在root page中有值,它也是有一个小小的结构:MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

可以看到root page的FSEG Header有两个指针,分别由一个number和offset组成。指向了该索引结构(B+树)的segment。这也呼应了我们之前说的一个索引结构会分配两个segment。

System records:一个索引页包含两个默认的记录,infimum和supremum,以便在遍历时快速定位到索引内记录的位置。

User records:实际的表数据。

抛开这些琐碎的结构,实际上一个索引页可以表示为如下MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

包含头,包含尾,包含数据。

站在全局的角度,我们再缩小点,索引的结构如下:MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

最上面是root page,中间是internal page,他们都是non leaf page。只存储最下面绿色的是leaf page,存放实际的数据。

那么这下索引的结构就清晰了。是一颗B+树。

当执行一条语句,Mysql是如何查找的呢?

从root page开始查询,root page包含了一个FSEG Header,通过FSEG Header找到叶子和非叶子索引的segment,然后通过segment,确定到对应extent以及page上,但会定位到page内具体的record。

这里引用一张图来描述这种关系(图片来自Jeremy Cole,MySQL资深工程师):MySQL的最深处-磁盘文件结构-鸿蒙开发者社区

总结

本文我们认识了Mysql的磁盘文件的结构,以及索引是如何在Mysql中存储的,从全局的角度看Mysql的存储结构,使读者对Mysql存储结构有一个宏观的感知,如果大家感兴趣的话,细节后续我会继续写。希望这篇文章对你能有帮助。

分类
标签
已于2022-9-15 11:48:42修改
收藏
回复
举报
回复
    相关推荐