跳转至

MySql存储结构

参考视频:MySql存储结构

1.表空间

不同的存储引擎在磁盘文件上的结构均不一致,这里以InnoDB为例:

CREATE TABLE t(id int(11)) Engine = INNODB;

在新表创建的过程中,InnoDB会在磁盘的data目录下创建与这个表对应的两个文件:t.frm、t.ibd。

  • t.frm 存储了表结构等信息,文件相对较小
  • t.ibd 就是常说的”表空间“文件,它用来存储表的数据和索引。文件大小取决于表中的数据量。

注意:只有在mysql5.7版本后才会为每个表生成一个ibd文件,称为**独立表空间**,在此之前所有表的数据和索引都会存储系统表空间中。**系统表空间**也被称为共享表空间,即所有表共享一个物理表空间文件。

在mysql8.0之后开始去掉了frm文件,表结构定义默认内置到InnoDB的ibd文件中

我们也可以通过全局参数 innodb file per table 来进行设置。

-- 查看每个表都创建表空间文件状态
SHOW VARIABLES LIKE 'innodb_file_per_table';
-- 开启"每个表都创建表空间文件功能"
SET @@global.innodb_file_per_table= ON;

表空间共分为五类,除了上面说的独立表空间,系统表空间,还有Undo表空间、通用表空间、临时表空间。在官方的的文档中,独立表空间相比于系统表空间,具有可压缩,可传输等优势。

2.页

在ibd文件中,最重要的结构体就是**”页“(Pages),即InnoDB中内存和磁盘交互的最小存储单元**。Mysql每次内存于磁盘交互数据,都会至少读写一个“页”的大小,因此在磁盘中每个“页”内部的地址都是连续的。

要查询的数据很多情况下都是连续存在的,因此存在这样的机制,只需在磁盘中读取一段连续的数据放入内存(Buffer Pool),后续的查询大概率可以直接从内存中找到。这样就减少了磁盘的访问次数,从而大大提升效率。这一段固定的连续的数据就被称为“页”。

页的大小为固定的16KB,即使没有数据也会占用16KB大小。这16KB的内容具体结构比较多样,在不同的场景会使用不同类型的“页”,一共有12种页类型。但无论什么类型的页均会包含**“页头”(File Header)和“页尾”(File Trailer)**,在页头和页尾之间的页的“主体信息”会根据不同的页类型由不同的结构。

image-20230317214241799

最为常用的就是用来存储数据和索引的“索引页”,它的主体信息会使用**数据“行”**进行填充。

-- 查看某表的行类型
SELECT t.SPACE,t.NAME,t.ROW_FORMAT FROM information_schema.INNODB_SYS_TABLESPACES

相比页的大小为固定值,行则不同,它是一个最大为8K但大小不固定的结构,内部主要包括表里某一行的真实数据和一些额外信息。

3.区

Mysql所有的表数据都会通过“行”、“页”的方式存储在磁盘中,但是每一个页只有16KB,当要存储的数据和读写量暴增时,跨“页”读取就变得再平常不过了。

如果多个页之间的物理距离过大,那多份数据在磁盘中就很有可能不在同一个磁道。为了读取数据,就会发生**磁头移动**,这种移动是物理摆动,相比磁片每分钟几千上万次的旋转读取要缓慢得多,所以磁头移动会大大降低性能。

需要尽可能在磁道上读取连续的数据,减少磁头的移动才能提升效率。因此**MySql还存在一个叫“区”的结构。**每个区都固定为1MB,存放64个连续地址的页,这样即使跨页读取相关数据,大概率都在附近的地址,减少了磁头移动,提高了效率。

于此同时,如果频繁地读取某个“区”内的”页“,Mysql就会将这个区中的所有数据读取出来,放入内存中,减少后续查询对磁盘的访问次数。

当然,在程序员创建新表时,由于不知道表未来的数据大小,为了不至于一次性占用过大的磁盘空间而导致浪费,所以在**新建一个表时只会创建6个“页”**,而不是一个完整的区,共占用16*6=96KB的大小。

image-20230317215914656

当然在mysql 8.0版本后初次会创建7个页

这些零散”页“会被放在表空间中一个叫碎片区的地方,解析了这6个页后可以看到它们各有不同,其中后两个页为空闲页,即可用页。前4个页分别记录了表空间和区组条目信息、Change buffer相关信息、段信息、索引根信息

image-20230317220207399

当要存储的数据越来越多,6个初始“页”空间不够用的时候,就需要一个一个地新增“页”来满足存储需求,当构建了32个零散”页“之后,后续每次都会直接申请完整的”区“来存储更多的数据。

image-20230317220548898

4.组

然而,当''区“的数量也越来越多时,为了有效地管理区,Mysql又会使用到“组”结构。

每一个“区组”管理固定的256个区,即256MB,它的结构比较简单,就是由256个区直接构成。其中第一个“区组”中的首个“区”的前四页比较特殊,就是之前所说的6个初始页中的前4个:即File Space Header、Insert Buffer Bitmap、File Segment inode、B-tree Node。

而其他区组中首个区的结构均一致,前两个页分别记录了区组条目信息,Change buffer信息,即Extent Descriptor(XDES)、Insert Buffer Bitmap

image-20230317221516739

InnoDB通过“区组”,可以在物理结构层面,非常高效地管理和定位到每个区

5.段

与区、区组这种物理结构不同,”段“是一个逻辑概念,并不对应表空间中连续的物理区域,可以看成区、页的一个附加的标注信息。

段的主要作用是用来区分不同功能的“区”和在碎片区中的”页“,分为”叶子节点段“和”非叶子节点段“等,这两个段与我们常说的B+树索引中的叶子、非叶子节点相对应,也可以简单地理解为“非叶子节点段”存储和管理索引树,“叶子节点段”存储和管理实际数据。

从逻辑上讲,最终由叶子节点段和非叶子节点段等段构成了最终的表空间ibd文件。

image-20230317222432519

image-20230317222535624