MySQL学习(5)记录存在那里——表空间
存储引擎都是把数据存储在文件系统上,通过通过查询命令,可以查看数据目录所在的本机路径。
mysql> SHOW VARIABLES LIKE 'datadir'; +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | datadir | /var/lib/mysql/ | +---------------+-----------------+ 1 row in set (0.00 sec)
每个数据库都在数据目录下有一个同名子目录,这是在新建数据库时,MySQL自动创建的。在数据库名对应的子目录下,每一张数据表都有一个同名以后缀名为.frm的文件,这个文件存储了表结构的定义。在InnoDB存储引擎中,表的数据存储在一个叫表空间的地方。
InnoDB页和表的关系
根据表空间的类型可以划分为如下:
-
系统表空间
InnoDB在数据目录创建的名为ibdata1的文件,这个文件可以自己扩展大小。一个MySQL进程只有一个系统表空间,其中记录了一些与整个系统相关的信息。
-
独立表空间
在数据库同名的子目录中,每一张数据表都有一个同名以后缀名为.ibd的文件。它与.frm文件共同分别存储了表的结构和记录。
独立表空间结构
什么是区(extent)
表空间中连续的64个页就是一个区,也就是一个区默认占用1MB空间。无论是系统表空间还是独立表空间,都可以看成是若干个连续的区组成,每256个区为一组。
如下extent0-extent255位一组,extent256-extent511位一组,以此类推。
第一组前面3个页始终是这三种页:
-
FSP_HDR:用于记录表空间的一些属性以及本组所有区的XDES Entry数据结构,也就是有256个。
-
IBUF_BITMAP:存储Change Buffer的一些信息。
-
INODE:存储了若干INODE Entry数据结构。
其余组前2个页始终是这两种:
-
XDES:存储本组所有区的XDES Entry数据结构。
-
IBUF_BITMAP:同上。
什么是段(segment)
同一个索引的B+树结构中,目录项所在的页,也就是非叶子节点所在的区的集合为一个段,叶子节点所在的区的集合为一个段。一个索引会生成两个段,一个叶子节点段和一个非叶子节点段。
表中数据较少时,每次新插入一条记录,都会被插入到一个碎片区中,当使用了32个碎片区中的页后,会分配一个完整的区,原先碎片区中的页不会被复制到新的完整的区中。因此段是某些零散页面以及一些完整的区的集合。
什么是碎片区(fragment)
碎片区也是由64个页组成,只属于表空间。结构上和其他的区没有啥不同。碎片区中的页用于不同目的,不限制于某个索引的某个段。有些页属于段A,有些页属于段B。
区的分类
4种类型:
-
空闲的区(FREE)
-
有剩余空闲页面的碎片区(FREE_FRAG)
-
没有剩余空闲页面的碎片区(FULL_FRAG)
-
属于某个段段区(FSEG)
其中FREE、FREE_FRAG和FULL_FRAG独立且直属于表空间;FSEG属于某个段。
什么是XDES Entry
XDES Entry是一种用于更好管理区的数据结构,每个区都对应一个XDES Entry结构,这个结构存放在每个组都FSP_HDR或XDES页中。
每个XDES Entry由40个字节构成,分为4个部分。
-
Segment ID表示所属段的编号,若不属于任何段,这个属性无意义。
-
List Node包含前一个XDES Entry和后一个XDES Entry的指针,通过这些指针可以构成双向链表结构。
-
State的值表示这个区的状态,也就是FREE、FREE_FRAG、FULL_FRAG或FSEG。
-
Page State Bitmap共16个字节,合128比特位,没两个比特位代表一个页。其中第1位表示对应的页是否空闲。
什么是XDES Entry链表
利用XDES Entry中的List Node,将相同类型的区串成双向链表,这么做的好处是减少随机I/O,又不至于让数据少的表浪费空间,避免每次想找到一种类型的区时要遍历所有的区。
-
FREE链表,包含不属于任何段,且所有页面都是空闲页面的区
-
FREE_FRAG链表,包含不属于任何段,且有剩余空闲页面的区
-
FULL_FRAG链表,包含不属于任何段,且没有剩余空闲页面的区
属于某个段的区中会根据List Node串成三种链表:
-
FREE链表,包含同一个段中,所有页main都是空闲页面的区
-
NOT_NULL链表,包含同一个段中,存在空闲页面的区
-
FULL链表,包含同一个段中,已经没有空闲页面的区
插入记录流程如下所示:
每一个链表的头节点和尾节点以及这个链表中包含了多少个节点的信息被记录在一个叫List Base Node链表基节点的结构中,占用16个字节。
碎片区中的链表基节点存放在FSP_HDR页中的File Space Header中,附属于段的链表基节点存放在INODE Entry结构中。
什么是INODE Entry
段不同于区,它是一个逻辑概念,由若干个零散页面和一些完整的区组成。与XDES Entry类似,每一个段有一个INODE Entry结构,存放在表空间第一组的INODE页。
INODE Entry结构如下:
-
Segment ID:段编号
-
NOT_FULL_N_USED:NOT_FULL链表中已经使用了页面数量
-
List Base Node For FREE List:FREE链表的基节点。
-
List Base Node For NOTE_FULL List:NOT_FULL链表的基节点。
-
List Base Node For FULL List:FULL链表的基节点。
-
Magic Number:用来标记这个INODE Entry是否已经被初始化,也就是把各个字段的信息都填上了。该值为97937874,表明该INODE Entry已被初始化,否则没有被初始化。
-
Fragment Array Entry:32个,对应着属于该段的零散页面的页号。
直属于表空间的区,每个区对应一个XDES Entry,根据区的状态分别建立3个链表,链表基节点存放在FSD_HDR页。附属于段段区,根据页面空闲状态,分别建立3个链表,链表基节点存放在INODE Entry。
什么是FSP_HDR页
一个表空间只有一个FSP_HDR页,其结构如下:
File Header、File Trailer与其他类型的页类似,主要关注File Space Header和XDES Entry。
-
File Space Header部分
File Space Header结构如下:
-
Space ID字段表示表空间编号。
-
Size字段表示当前表空间拥有的页面数。
-
FREE Limit字段表示未被初始化的最小页号。空间增大时,不会立即将空闲空间加入到FREE链表,只将一部分加入到FREE链表,等FREE链表中的XDES Entry对应的区不够用时,再把没有加入FREE链表的区对应的XDES Entry加入到FREE链表,FREE Limit表示还未加入到FREE链表的最小页号。
-
Space Flags表示表空间中的一些状态属性。
-
FRAG_N_USED字段表示FREE_FRAG链表中已经使用的页面数量。
-
List Base Node for FREE List、List Base Node for FREE_FRAG List、List Base Node for FULL_FRAG List着3个字段分别表示FREE链表、FREE_FRAG链表和FULL_FRAG链表的基节点。使得定位链表更容易。
-
NEXT Unused Segment ID字段表示下一个新建段的ID,类似于自增主键。
-
List Base Node for SEG_INODES_FULL字段表示SEG_INODES_FULL链表的基节点。
-
List Base Node for SEG_INDOES_FREE字段表示SEG_INODES_FREE链表的基节点。
-
XDES Entry部分
每个区对应一个XDES Entry结构,一个XDES Entry占用40个字节,一个组有256个组,表空间第一组中的所有区对应的XDES Entry就存储在FSP_HDR页的XDES Entry部分中。
什么是XDES页
XDES页和FSP_HDR页结构相似,区别在于XDES页没有了FIle Space Header部分。功能就是存储本组中所有区对应的256个XDES Entry。
什么是IBUF_BITMAP页
每个组第二个页都是IBUF_BITMAP,这种类型的页记录了有关Change Buffer的东西。
Change Buffer本质是表空间中的一颗B+树,它的根节点存储在系统表空间中。在修改非唯一二级索引页面时,如果该页尚未被加载到内存,那么该修改将被暂时缓存在Change Buffer中,之后服务器空闲或加载了该页到内存中,再将修改和冰岛该页面。
什么是INODE页
第一个分组中第三个页面的类型是INODE,其结构如下:
List Node for INODE Page List为前一个INODE页和后一个INODE页的指针,每个段对应的INODE Entry结构会急中存放在INODE页中,每个INODE Entry占用192字节,一个INODE页可以存放85个INODE Entry结构。如果表中索引特别多,每个索引对应两个段,所以一个INODE页不够用,就会使用多个INODE页来进行存储。这些INODE页会形成两个链表。
-
SEG_INODES_FULL链表:已经没有空闲空间用来存储INODE Entry的INODE页所建立的链表。
-
SEG_INODES_FREE链表:还有剩余空间用来存储INODE Entry的INODE页所建立的链表。
什么是Segment Header
为了知道某个段对应哪个INODE Entry结构,在段中的记录所在的页,也就是INDEX页Page Header部分有这样两个属性:
名称 | 大小 | 描述 |
---|---|---|
PAGE_BTR_SEG_LEAF | 10字节 | B+树叶子节点段段头部信息,仅在B+树段根页中定义 |
PAGE_BTR_SEG_TOP | 10字节 | B+树非叶子节点段的头部信息,仅在B+树的根页中定义 |
这10个字节对应的是一个Segment Header结构:
名称 | 大小 | 描述 |
---|---|---|
Space ID for INODE Entry | 4字节 | INODE Entry结构所在的表空间ID |
Page Number of the INODE Entry | 4字节 | INODE Entry结构所在的页面页号 |
Byte Offset of the INODE Entry | 2字节 | INODE Entry结构在该页中偏移量 |
这样,PAGE_BTR_SEG_LEAF记录着叶子节点段对应的INODE Entry结构的地址,PAGE_BTR_SEG_TOP记录着非叶子节点对应的INODE Entry结构的地址。一个索引值对应两个段,所在只需要在索引的根页面记录这两个结构即可。
阅读学习自《MySQL是怎样运行的》小孩子4919
热门相关:重生之女将星 精灵掌门人 一等狂妃:邪王,请接招! 网游三国之城市攻略 楚氏赘婿