数据目录

文件系统中的表

文件系统中的,表一般由两部分存在文件中:
1.表结构
2.表数据
所有的存储引擎,都会把表名定义一个对应的文件名.frm

InnoDB是如何存储表数据的

InnoDB存储表数据的时候,对应的会生成一个.ibd文件,注意是把所有的表数据和表索引都存放在.ibd文件中,因为InnoDB采用数据列作为索引

MyISAM是如何存储表数据的

MyISAM存储表数据的时候,会生成一个.MYD文件来存放表数据,再生成一个.MYI来存表索引。也是因为MyISAM的索引是记录行号作为索引

视图在⽂件系统中的表⽰

mysql的视图实际并不是一个数据表,而是一个查询语句的别名。所以在文件系统中不用存数据,存返回的结构就可以了

⽂件系统对数据库的影响

1.文件系统的大小决定数据库的存储容量
2.数据库的命名按文件的命名要求,会转化特殊字符
3.数据库命名长度也受限制

MySQL系统数据库简介

1.mysql:存放了这个数据库的账户权限用户信息,还包括存储过程,事件定义
2.information_schema:存放的内容不是真实的用户数据,而是元信息,有哪些表那些列那些索引
3.performance_schema:主要用来存放数据库实际运行过程中的状态信息,记录某个查询的时间监控性能
4.sys:通过视图的方式,结合了2,3的内容,做统计分析用

独立表空间

独立表空间实际对应着⽂件系统中⼀个名为表名.ibd的实际⽂件,当我们往数据库某个表中插入数据的时候,实际是从找到独立表空间系统中的某个页,完成新增数据的过程

InnerDB表空间

InnerDB是以页为单位管理存储空间的,我们的聚簇索引(也就是完整的表数据)和其他的⼆级索引都是以B+树的形式保存到表空间的,⽽B+树的节点就是数据页。这个数据 页的类型名其实是:FIL_PAGE_INDEX

回顾一下通用页的内容:页尾做校验和

mysql 空间类型数据查询方式 mysql表空间名_数据库


页头部(File Header):记录上一个和下一个页,记录当前页的类型,记录页所属的表空间等内容,记录页号4个字节,也就是说最大页2的32次,按每页16kb的大小,一个表空间最大存储64TB数据。

区是一个逻辑概念,是用来形容一块连续的页区域。一个组由256个区组成,一个区由连续的64个页组成。所以一个区的大小1Mb

盗个图源于从根儿上学习mysql:

mysql 空间类型数据查询方式 mysql表空间名_表空间_02


extent 0 - extent 255算是第一个分组,extent0的前三个页如图所示

FSP_HDR :这个类型的页⾯是⽤来登记整个表空间的⼀些整体属性以及本组所有的区,也就是extent 0 ~ extent 255这256个区的属性,表空间内只有一个FSP_HDR页

IBUF_BITMAP:这个类型的页⾯是存储本组所有的区的所有页⾯关于INSERT BUFFER的信息

INODE:这个类型的页⾯是整个表空间所有INODE信息

extent 256 - extent 511算是第二个分组,extent256的前两个页

XDES:⽤来登记本组256个区的属性,

IBUF_BITMAP:这个类型的页⾯是存储本组所有的区的所有页⾯关于INSERT BUFFER的信息为啥会有区的概念?

数据页之间在内存中不一定是连续的区域,范围查询的时候,可能是一个随机IO的操作,那么我们为了更好的管理数据,以区来存一段数据页,扩展一下currentHashMap在hashMap的基础上增加了一个分段锁的概念,在我的理解上也是为了分段式的去管理数据集合,大同小异,数据量小的情况下可能效果并不好。所以mysql提供区的概念,还提供了一个碎片区的逻辑,目的就是为了解决,表空间数据量小的情况下,不需要分配一个专门的区来做数据存储。

表空间有哪些区?

FREE 空闲的区

FREE_FRAG 有剩余空间的碎⽚区

FULL_FRAG 没有剩余空间的碎⽚区

FSEG 附属于某个段的区 需要再次强调⼀遍的是,处于FREE

如何管理这些区?

为了⽅便管理这些区,设计InnoDB的⼤叔设计了⼀个称为XDES Entry的结构(全称就是Extent Descriptor Entry)。

mysql 空间类型数据查询方式 mysql表空间名_mysql 空间类型数据查询方式_03


简单介绍一下:segment ID如果这个区属于某个段,记录的就是段ID,List Node就是记录前一个区后一个区,state来表示这个区是什么类型的区,Page State Bitmap由16个字节,128bit组成,前面我们说过64个页组成一个区,那么每个数据页用2bit来表示记录状态,我们可以想象如何算区是否空闲,就是根据数据页第一位bit来查看状态,如果都是满的话,区就没有剩余空间了。

介绍完区就来简单描述一下段的概念,段也是一个逻辑概念,我们描述了区是一个存放许多页的单位,那么段就是更有目的性的管理区,建立一个索引,会分为两个段,一个是存放非叶子结点的段,存放非叶子结点区的集合,另一个是存放叶子结点的段,存放叶子结点区的集合。

段中插入数据的过程

当段中的数据表较少时
会去找系统表中的碎片区,找FREE_FRAG 有剩余空间的碎⽚区,往里插入数据,该碎片区满了之后,这个碎片区就变成了FULL_FRAG,找下一个FREE_FRAG的碎片区,这时候我们很容易想到链表的方式找第二个FREE_FRAG的碎片区,因为区里有记录上下区的地址,直到所有的FREE_FRAG都没了,这时候我们就从FREE里找,如果FREE页也是按照同一个思路,通过FREE的链表查。
表空间碎片区最重要的三个XDES Entry链表:FULL_FRAG链表,FREE链表,FREE_PAGE链表
当表中的数据占满了32个零散的页后
这时候表空间会分配一个完整的区,用来存储数据。所以这些区都是属于这个段的内容,那我们怎么找xdes entry的区呢,同样的所属于段的区,也分为了三个链表。
FREE链表:同⼀个段中,所有页⾯都是空闲的区对应的XDES Entry结构会被加⼊到这个链表。注意和直属于表空间的FREE链表区别开了,此处的FREE链表是附属于某个段的。
NOT_FULL链表:同⼀个段中,仍有空闲空间的区对应的XDES Entry结构会被加⼊到这个链表。
FULL链表:同⼀个段中,已经没有空闲空间的区对应的XDES Entry结构会被加⼊到这个链表。

当我们新建一张表的时候,带了聚簇索引,产生了两个段,每个段内有3个链表,表空间还有三个链表。总共就有9个链表结构了

链表基节点

我们产生了这么多的链表,链表的头和尾存在哪里,如何找到?

mysql设计了List Base Node的结构,翻译成中⽂就是链表的基节点。

mysql 空间类型数据查询方式 mysql表空间名_数据库_04

段的设计结构

每个区都有对应的XDES Entry来记录这个区中的属性⼀样,设 计InnoDB的⼤叔为每个段都定义了⼀个INODE Entry结构来记录⼀下段中的属性

mysql 空间类型数据查询方式 mysql表空间名_mysql 空间类型数据查询方式_05


Segment ID记录段的ID

NOT_FULL_N_USED记录当前最小没有用到的页,下一次插入就从这里开始

中间三个基节点就是上面提到的段中的区对应的三个基节点

Magic Number貌似来标记是够被初始化的一个固定值

Frament Array Entry存放的就是该段的碎片区,每个Fragment Array Entry结构都对应着⼀个零散的页⾯,这个结构⼀共4个字节,表⽰⼀个零散页⾯的页号

详解FSP_HDR

mysql 空间类型数据查询方式 mysql表空间名_学习_06


File Header:页的⼀些通⽤信息

File Space Header:关键部门,记录了表空间ID,记录了表空间占用了页面数,尚未被初始化的最⼩页号,FREE_FRAG链表中已使⽤的页⾯数量,三个表空间链表的基节点,当前表空间中下⼀个未使⽤的 Segment ID,还有SEG_INODES_FULL链表的基节点和SEG_INODES_FREE链表的基节点

XDES ENTRY:我们知道⼀个XDES Entry结构的⼤⼩是40字节,但 是⼀个页⾯的⼤⼩有限,只能存放有限个XDES Entry结构,所以我们才把256个区划分成⼀组,在每组的第⼀个页⾯中存放256个XDES Entry结构。⼤家回看那个FSP_HDR类型页⾯的⽰意图,XDES Entry 0就对应着extent 0,XDES Entry 1就对应着extent 1… 依此类推,XDES Entry255就对应着extent 255。

详解XDES

和FSP_HDR结构相似

mysql 空间类型数据查询方式 mysql表空间名_mysql_07

详解IBUF_BITMAP

每个分组的第⼆个页⾯的类型都是IBUF_BITMAP,这种类型的页⾥边记录了⼀些有关Change Buffer的

详解INODE

mysql 空间类型数据查询方式 mysql表空间名_表空间_08

INODE页中存储了85个INODE Entry结构,我们知道一个表中可能不止要85个段,如何管理?

这里主要再描述一下为啥header里面有两个段链表,为了⽅便管理这些INODE类型的页⾯,设计InnoDB的⼤叔们将这些INODE类型的页⾯串联成两个不同的链表: SEG_INODES_FULL链表:该链表中的INODE类型的页⾯中已经没有空闲空间来存储额外的INODE Entry结构了。 SEG_INODES_FREE链表:该链表中的INODE类型的页⾯中还有空闲空间来存储额外的INODE Entry结构了。

如何知道那个INODE Entry对应了那个段?

数据页中:

PAGE_BTR_SEG_LEAF 10字节 B+树叶⼦段的头部信息,仅在B+树的根页定义

PAGE_BTR_SEG_TOP 10字节 B+树⾮叶⼦段的头部信息,仅在B+树的根页定义

mysql 空间类型数据查询方式 mysql表空间名_mysql 空间类型数据查询方式_09


Segment Header记录了

Space ID of the INODE Entry INODE Entry结构所在的表空间ID

Page Number of the INODE Entry INODE Entry结构所在的页⾯页号

Byte Offset of the INODE Ent INODE Entry结构在该页⾯中的偏移量

系统表空间

了解完了独⽴表空间的基本结构,系统表空间的结构也就好理解多了,系统表空间的结构和独⽴表空间基本类似,只不过由于整个MySQL进程只有⼀个系统表空间,在系统表空间中会额外记录⼀些有关 整个系统信息的页⾯,所以会⽐独⽴表空间多出⼀些记录这些信息的页⾯。因为这个系统表空间最⽜逼,相当于是表空间之⾸,所以它的表空间 ID(Space ID)是0。

系统表结构

mysql 空间类型数据查询方式 mysql表空间名_表空间_10

系统表特有的页

3 SYS Insert Buffer Header 存储Insert Buffer的头部信息
4 INDEX Insert Buffer Root 存储Insert Buffer的根页⾯
5 TRX_SYS Transction System 事务系统的相关信息
6 SYS First Rollback Segment 第⼀个回滚段的页⾯
7 SYS Data Dictionary Header 数据字典头部信息
除了这⼏个记录系统属性的页⾯之外,系统表空间的extent 1和extent 2这两个区,也就是页号从64~191这128个页⾯被称为Doublewrite buffer,也就是双写缓冲区。不过上述的⼤部分知识都涉及到了 事务和多版本控制的问题。

数据字典

上述这些数据并不是我们使⽤INSERT语句插⼊的⽤户数据,实际上是为了更好的管理我们这些⽤户数据⽽不得已引⼊的⼀些额外数据,这些数据也称为元数据。InnoDB存储引擎特意定义了⼀些列的内部系 统表(internal system table)来记录这些这些元数据:
表名 描述
SYS_TABLES 整个InnoDB存储引擎中所有的表的信息
SYS_COLUMNS 整个InnoDB存储引擎中所有的列的信息
SYS_INDEXES 整个InnoDB存储引擎中所有的索引的信息
SYS_FIELDS 整个InnoDB存储引擎中所有的索引对应的列的信息
SYS_FOREIGN 整个InnoDB存储引擎中所有的外键的信息
SYS_FOREIGN_COLS 整个InnoDB存储引擎中所有的外键对应列的信息
SYS_TABLESPACES 整个InnoDB存储引擎中所有的表空间信息
SYS_DATAFILES 整个InnoDB存储引擎中所有的表空间对应⽂件系统的⽂件路径信息
SYS_VIRTUAL 整个InnoDB存储引擎中所有的虚拟⽣成列的信息

⽤户是不能直接访问InnoDB的这些内部系统表的,除⾮你直接去解析系统表空间对应⽂件系统上的⽂件
mysql系统数据库information_schema中提供了⼀些以innodb_sys开头的表,查到的并不是系统表,字段列表有一些区别
SHOW TABLES LIKE ‘innodb_sys%’;

7SYS Data Dictionary Header页⾯

那这些个表的元数据去哪⾥获取呢?

没法搞了,只能把这4个表的元数据,就是它们有哪些列、哪些索引等信息硬编码到代码中,然后设计InnoDB的⼤叔又拿出⼀个固定的页 ⾯来记录这4个表的聚簇索引和⼆级索引对应的B+树位置,这个页⾯就是页号为7的页⾯,类型为SYS,记录了Data Dictionary Header,也就是数据字典的头部信息。除了这4个表的5个索引的根页⾯信息 外,这个页号为7的页⾯还记录了整个InnoDB存储引擎的⼀些全局属性,说话太啰嗦,直接看这个页⾯的⽰意图

mysql 空间类型数据查询方式 mysql表空间名_数据库_11


Data Dictionary Header :数据字典头部信息,记录⼀些基本系统表的根页⾯位置以及InnoDB存储引擎的⼀些全局信息

Segment Header: 段头部信息 10字节 记录本页⾯所在段对应的INODE Entry位置信息

接下来我们需要细细唠叨⼀下Data Dictionary Header部分的各个字段:
Max Row ID:我们说过如果我们不显式的为表定义主键,⽽且表中也没有UNIQUE索引,那么InnoDB存储引擎会默认为我们⽣成⼀个名为row_id的列作为主键。因为它是主键,所以每条记录 的row_id列的值不能重复。原则上只要⼀个表中的row_id列不重复就可以了,也就是说表a和表b拥有⼀样的row_id列也没啥关系,不过设计InnoDB的⼤叔只提供了这个Max Row ID字段,不论哪个拥 有row_id列的表插⼊⼀条记录时,该记录的row_id列的值就是Max Row ID对应的值,然后再把Max Row ID对应的值加1,也就是说这个Max Row ID是全局共享的。
Max Table ID:InnoDB存储引擎中的所有的表都对应⼀个唯⼀的ID,每次新建⼀个表时,就会把本字段的值作为该表的ID,然后⾃增本字段的值。
Max Index ID:InnoDB存储引擎中的所有的索引都对应⼀个唯⼀的ID,每次新建⼀个索引时,就会把本字段的值作为该索引的ID,然后⾃增本字段的值。
Max Space ID:InnoDB存储引擎中的所有的表空间都对应⼀个唯⼀的ID,每次新建⼀个表空间时,就会把本字段的值作为该表空间的ID,然后⾃增本字段的值。
Mix ID Low(Unused):这个字段没啥⽤,跳过。
Root of SYS_TABLES clust index:本字段代表SYS_TABLES表聚簇索引的根页⾯的页号。
Root of SYS_TABLE_IDS sec index:本字段代表SYS_TABLES表为ID列建⽴的⼆级索引的根页⾯的页号。
Root of SYS_COLUMNS clust index:本字段代表SYS_COLUMNS表聚簇索引的根页⾯的页号。
Root of SYS_INDEXES clust index本字段代表SYS_INDEXES表聚簇索引的根页⾯的页号。
Root of SYS_FIELDS clust index:本字段代表SYS_FIELDS表聚簇索引的根页⾯的页号。