InnoDB-表空间

前言

大家好,我是xicheng。现在继续更新MySQL,本篇讲InnoDB的表空间,该部分类容比较枯燥繁琐,但又是MySQL后续内容的基础。所以大家可以先学习理解整体框架,等后续篇章用到的时候,再回过头查阅,进一步加深理解。另外,InnoDB的知识脑图如下所示,大家坐稳了。


表空间

表空间(tablespace)由段(sagment)组成,段由区(extent)组成,区由页(page)组成,页由行组成。如下图所示。

所有数据都存放在表空间中。如果用户手动启用了参数innodb_file_per_table,则每张表的数据可以单独放在一个表空间中。

逻辑上的概念,⼀个索引会⽣成2个段,⼀个叶⼦节点段(存放叶⼦节点的区),⼀个⾮叶⼦节点段(存放⾮叶⼦节点的区)。

在刚开始向表中插⼊数据的时候,段是从某个碎⽚区(并不是所有页都是存储一个段的数据的区)以⻚为单位来分配存储空间的。

当某个段已经占⽤了32个碎⽚区⻚⾯之后,就会以完整的区为单位来分配存储空间(原先占用的碎片区的页不会被复制到新的区中来)。

常见的段由数据段,索引段,回滚段等。

区结构

对于16KB的页,物理位置连续的64个页就是一个区(extend),大小1MB。256个区被划分为1个组。

每个组中第一个区的固定页如下图所示。

第一组中第0区开始的3个⻚的类型是固定的:

  • FSP_HDR(16KB):整个表空间的⼀些整体属性以及本组所有的区,整个表空间只有⼀个该类型的⻚⾯。
  • IBUF_BITMAP(16KB):本组所有的区的所有⻚⾯关于INSERT BUFFER的信息。
  • INODE(16KB):存储了许多 INODE 的数据结构。

其余各组最开始的 2 个⻚⾯的类型是固定的:

  • XDES ( extent descriptor):本组 256 个区的属性。
  • IBUF_BITMAP:存储本组所有的区的所有⻚⾯关于 INSERT BUFFER 的信息。
  • 在表中数据量⼤的时候,为某个索引分配空间的时候就不再按照⻚为单位分配了,⽽是按照区为单位分配。

区分类

空闲的区:FREE,还没有⽤到这个区中的任何⻚⾯。

有剩余空间的碎⽚区:FREE_FRAG,表示碎⽚区中还有可⽤的⻚⾯。

没有剩余空间的碎⽚区:FULL_FRAG,表示碎⽚区中的所有⻚⾯都被使⽤,没有空闲⻚⾯。

附属于某个段的区:FSEG。

区的XDES Entry

结构

为了方便管理区而设计的。共40个字节,分为4个部分。

  • SegmentID(8字节):段唯一编号,表示就是该区所在的段(前提是该区已被分配给某段了,否则该字段无意义)。
  • ListNode(12字节):PreNodePageNumber(4字节,前一页的页号)和PreNodeOffset(2字节,前一页的页号在页内的偏移量)指向前⼀个XDESEntry。NextNodePageNumber(4字节,后一页的页号)和NextNodeOffset(2字节,后一页的页号在页内的偏移量)指向后⼀个XDESEntry。
  • State:区的状态。参见“InnoDB表空间-区分类”条目。
  • PageStateBitmap:128个⽐特位。每2个⽐特位对应区中的⼀个⻚。第⼀个位表示对应的⻚是否是空闲的(1空闲。0不空闲),第⼆个⽐特位还没有⽤(1没用。0用了)。

XDES Entry链表

通过List Node把FREE区对应的XDES Entry链接成一个链表,叫FREE链表。同一段中所有页面是空闲的区的XDES Entry会被加到这个链表。

通过List Node把FREE_FRAG区对应的XDES Entry链接成一个链表,叫FREE_FRAG链表。同一段中还有空闲页面区的XDES Entry会被加到这个链表。

通过List Node把FULL_FRAG区对应的XDES Entry链接成一个链表,叫FREE_FRAG链表。同一段中没有空闲页面区的XDES Entry会被加到这个链表。

每个XDES Entry链表会有一个List Base Node节点,会被放在段的INODE Entry。其中。

  • ListLength:该链表总节点数。

  • FirstNodePageNumber和FirstNodeOffset:该链表的头节点在表空间中的位置。

  • LastNodePageNumber和LastNodeOffset:该链表的尾节点在表空间中的位置。

段的INODE Entry

为了方便管理段而设计的。共192字节,被分为如下几个部分。

  • Segment ID(8字节):该INODE Entry对应的段号。
  • NOT_FULL_N_USED(4字节):NOT_FULL链表的各XDES Entry节点对应的区已经使⽤了多少⻚⾯。⼀个区中有64个⻚⾯,如果不标记已经使⽤了多少⻚⾯的话,每次向段中插⼊数据的时候都要从第⼀个⻚⾯进⾏遍历寻找空闲⻚⾯,有了这个字段之后就可以快速定位空闲⻚⾯。
  • 3个List Base Node(分别都为16字节):分别为段的FREE链表、NOT_FULL链表、FULL链表定义了ListBaseNode。
  • Magic Number:标记这个INODE Entry是否已经被初始化了(值是97937874,表明已经初始化,否则没有被初始化)。
  • Fragment Array Entry:段是由零散的页面和完整的区组成。每个Fragment Array Entry结构都对应着⼀个零散的⻚⾯,这个结构⼀共4个字节,表示⼀个零散⻚⾯的⻚号。

页类型

  • FSP_HDR类型

表空间的第一个页面,也是第一个组的第一个页面,页号为0,存储表空间的整体属性及第一个组内内256区对应的XDES Entry结构。如下表所示。

名称 描述 占用空间(字节) 作用
File Header ⽂件头部 38 页的通用信息
File Space Header 表空间头部 112 表空间的⼀些整体属性信息
XDES Entry 区描述信息 10240 存储本组256个区对应的属性信息
Empty Space 尚未使⽤ 空间 5986 ⻚结构的填充
File Trailer ⽂件尾部 8 校验⻚是否完整

File Space Header如下表所示。

名称 占用空间(字节) 描述
Space ID 4 表空间的ID
Not Used 4 未使⽤
Size 4 当前表空间占有的⻚⾯数
FREE Limit 4 尚未被初始化的最⼩⻚号,⼤于或等于这个⻚号的区对应的XDES Entry结构都没有被加⼊FREE链表
Space Flags 4 表空间的⼀些占⽤存储空间⽐较⼩的属性,不同MySQL版本有些差异
FRAG_N_USED 4 FREE_FRAG链表中已使⽤的⻚⾯数量
3个List Base Node 16/16/16 FREE/FREE_FREG/FULL_FREG链表的基节点
Next Unused Segment ID 8 当前表空间中下⼀个未使⽤的Segment ID
List Base Node for SEG_INODES_FULL List 16 SEG_INODES_FULL链表的基节点
List Base Node for SEG_INODES_FREE List 16 SEG_INODES_FREE链表的基节点
  • XDES类型

除了第一个分组的第一个页面是FSP_HDR类型之外,之后的每个分组的第⼀个⻚⾯只需要记录本组内所有的区对应的XDES Entry结构即可。就叫它XDES类型。如下图所示。

  • IBUF_BITMAP类型

每个分组的第⼆个⻚⾯的类型都是这种类型,这种类型的⻚⾥边记录了⼀些有关Change Buffer的信息。本质是一颗B+树。

在修改非唯一二级索引页面时,如果页面尚未被加载到内存中,那么该修改会被暂时存储到Change Buffer中,等服务器空闲或者对应页面从磁盘加载到内存时,再将修改合并到对应的页面。

  • INDOE类型

第⼀个分组的第三个⻚⾯。记录段的相关信息,如下表所示。

名称 描述 占用空间(字节) 作用
File Header ⽂件头部 38 页的通用信息
List Node for INODE Page List 通用链表节点 12 存储上下两个INODE页面的指针。如果一个表空间超过85个INODE Entry,则需要额外的该类型页面来存储。
INODE Entry 段描述信息 16320 具体的INODE Entry结构
Empty Space 尚未使⽤空间 6 ⻚结构的填充
File Trailer ⽂件尾部 8 校验⻚是否完整

INDOE类型页面被划分为两个链表:

  • SEG_INODES_FULL链表:该链表中的INODE类型的⻚⾯中已经没有空闲空间来存储额外的INODE Entry结构了。
  • SEG_INODES_FREE链表:该链表中的INODE类型的⻚⾯中还有空闲空间来存储额外的INODE Entry结构了。

新建段时,会创建INODE Entry,存储INODE Entry的过程如下:

  1. 先看SEG_INODES_FREE链表是否为空,若不为空,直接从该链表中获取节点(页面),并把新的INDODE Entry放进去。当节点(页面)无空余空间时,就把该节点(页面)放到SEG_INODES_FREE中去。
  2. 若SEG_INODES_FREE为空,则需要从表空间的FREE_FRAG链表中申请一个页面,并将该页面的类型修改为INODE,并加入SEG_INODES_FREE链表,然后把INODE Entry结构放入该页面。

Segement Header结构

数据页的Page Header中有这两个字段:PAGE_BTR_SEG_LEA(10字节,B+树叶⼦节点段的头部信息,仅在B+树的根⻚定义),PAGE_BTR_SEG_TOP(10字节,B+树⾮叶⼦段的头部信息,仅在B+树的根⻚定义)。

这俩字段对应一个Segment Header组成,如下图所示。

名称 占用空间(字节) 描述
Space ID of the INODE Entry 4 INODE Entry结构所在的表空间ID
Page Number of the INODE Entry 4 INODE Entry结构所在的⻚⾯⻚号
Byte Offset of the INODE Entry 2

系统表空间

独立表空间用于记录用户数据(上述内容都是讲的独立表空间),系统表空间用于记录一些与整个系统有关的信息。
系统表空间表空间ID(SpaceID)是0。

第一个区的前三个页面与独立表空间是一致的,但第4个页面到第8个页面(页号从3到7)是系统表空间独有的,如下表所示。

页号 页面类型 英文名称 作用
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 数据字典头部信息,下文会讲到

后续区的前两个页面与独立表空间对应的区的页面是一致的。

元数据

更好地管理用户数据而引入的额外数据称为元数据。

记录元数据的系统表如下表所示。用户不能直接访问InnoDB的系统表。

表名 作用
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存储引擎中所有的虚拟⽣成列的信息

如下四个表尤为重要,且这四个表的元数据硬编码到代码中了。

  • SYS_TABLES表

(NAME为主键,ID列为二级索引)

表名 作用
NAME 表名
ID InnoDB存储引擎中每个表都有⼀个唯⼀的ID
N_COLS 该表拥有列的个数
TYPE 表的类型,记录了⼀些⽂件格式、⾏格式、压缩等信息
MIX_ID 忽略
MIX_LEN 忽略
CLUSTER_ID 忽略
SPACE 该表所属表空间的ID
  • SYS_COLUMNS表

(以TABLE_ID,POS为主键)

表名 作用
TABLE_ID 该列所属表对应的ID
POS 该列在表中是第⼏列
NAME 列的名称
MTYPE 表的类型,记录了⼀些⽂件格式、⾏格式、压缩等信息
PRTYPE 主数据类型,例如INT、VARCHAR
LEN 该列最多占⽤存储空间的字节数
PREC 忽略
  • SYS_INDEXES表

(TABLE_ID,ID为主键)

表名 作用
TABLE_ID 该索引所属表对应的ID
ID InnoDB存储引擎中每个索引都有⼀个唯⼀的ID
N_FIELDS 该索引包含列的个数
TYPE 索引的类型,⽐如聚簇索引、唯⼀索引
SPACE 该索引根页面所在的表空间ID
PAGE_NO 该索引根页面所在页号
MERGE_THRESHOLD 如果⻚⾯中的记录被删除MERGE_THRESHOLD,就把该⻚⾯和相邻⻚⾯合并
  • SYS_FIELDS表

(INDEX_ID,ID为主键)

表名 作用
INDEX_ID 该列所属索引的ID
POS 该列在索引中是第几列
COL_NAME 列名

Data Dictionary Header页面:用固定的⻚⾯来记录上述4个表的聚簇索引和⼆级索引对应的B+树位置,这个⻚⾯就是⻚号为7的⻚⾯。如下图与表所示。

名称 描述 占用空间(字节) 作用
File Header ⽂件头部 38 页的通用信息
Data Dictionary Header 数据字典头部 52 记录⼀些基本系统表的根⻚⾯位置以及InnoDB存储引擎的⼀些全局信息
Unused 未使用 4 未使用
Segment Header 段头部 10 记录本⻚⾯所在段对应的INODEEntry位置信息
Empty Space 尚未使⽤空间 6 ⻚结构的填充
File Trailer ⽂件尾部 8 校验⻚是否完整

Data Dictionary Header详解

  • MaxRowID:不论哪个拥有row_id列(InnoDB隐藏列)的表插⼊⼀条记录时,该记录的row_id列的值就是MaxRowID对应的值,然后再把MaxRowID对应的值加1,也就是说这个MaxRowID是全局共享的。
  • MaxTableID:InnoDB存储引擎中的所有的表都对应⼀个唯⼀的ID,每次新建⼀个表时,就会把本字段的值作为该表的ID,然后⾃增本字段的值。
  • MaxIndexID:InnoDB存储引擎中的所有的索引都对应⼀个唯⼀的ID,每次新建⼀个索引时,就会把本字段的值作为该索引的ID,然后⾃增本字段的值。
  • MaxSpaceID:InnoDB存储引擎中的所有的表空间都对应⼀个唯⼀的ID,每次新建⼀个表空间时,就会把本字段的值作为该表空间的ID,然后⾃增本字段的值。
  • MixIDLow(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表聚簇索引的根⻚⾯的⻚号。

结尾

InnoDB的表空间就讲完了,希望大家能持续学习。下一篇MySQL文章讲InnoDB-数据目录。

感谢各位人才的点赞、收藏和评论,干货文章持续更新中,下篇文章再见!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容