页面类型
类型名称(FIL_PAGE_) | 十六进制 | 描述 |
---|---|---|
TYPE_ALLOCATED | 0x0000 | 最新分配,还未使用 |
UNDO_LOG | 0x0002 | undo日志页 |
INODE | 0x0003 | 存储段的信息 |
IBUF_FREE_LIST | 0x0004 | Change Buffer 空闲列表 |
IBUF_BITMAP | 0x0005 | Change Buffer 的一些属性 |
TYPE_SYS | 0x0006 | 存储一些系统数据 |
TYPE_TRX_SYS | 0x0007 | 事务系统数据 |
TYPE_FST_HDR | 0x0008 | 表空间头部信息 |
TYPE_XDES | 0x0009 | 存储区的一些属性 |
TYPE_BLOB | 0x000A | 溢出页 |
INDEX | 0x45BF | 索引页,或说数据页 |
9.2 独立表空间结构
连续64个页组成一个区,所以一个区占用1MB空间,每256个区被划分一组
- 第一个组最开始的3个页面的类型是固定的
FSP_HDR
:登记整个表空间的一些整体属性以及本组所有区(extent 0~255)的属性IBUF_BITMAP
:存储关于Change Buffer的一些信息INODE
:存储了INODE Entry的数据结构
- 其余各组最开始的2个页面类型是固定的
XDES
:用来登记本组256个区的属性IBUF_BITMAP
:同上
表空间被划分为许多连续的区(区里页面的页号是连续的),每个区默认由64个页组成,每256个区划分为一组,每个组的最开始的几个页面类型是固定的
9.2.2 段的概念
分为叶子节点段和非叶子节点段
在表中数据量很大时,为某个索引分配空间的时候就不再按照页为单位分配了,而是按照区为单位进行分配
如果数据量较小,就分配在碎片区中,碎片区直属于表空间
所以为某个段分配存储空间的策略分2步:
在刚开始向表中插入数据时,段是从某个碎片区以单个页面为单位来分配存储空间的
当某个段已经占用了32个碎片区页面之后,就会以完整的区为单位来分配存储空间,原先占用的碎片区页面并不会被复制到新申请的完整的区中
9.2.3 区的分类
- 空闲的区(
FREE
) - 有剩余空闲页面的碎片区(
FREE_FRAG
) - 没有剩余空闲页面的碎片区(
FULL_FRAG
) - 附属于某个段的区(
FSEG
)
XDES Entry
(Extent Descriptor Entry)数据结构可以用来管理这些区,描述每个区的属性
总共40字节,包括4部分
Segment ID
(8字节):每个段的编号List Node
(12字节):将多个XDES Entry结构串成一个链表State
(4字节):表明区的状态,即FREE/FREE_FRAG/FULL_FRAG/FSEGPage State Bitmap
(16字节):128位,每2位对应一个页面,所以总共是64个页面,第1位表示页是否空闲,第2位还没用到
1、XDES Entry
链表
向表中插入数据本质上就是向表中各个索引的叶子节点段、非叶子节点段插入数据
当段中数据较少时,首先会查看表空间中是否有状态为FREE_FRAG
的区(也就是查找还有空闲页面的碎片区)。如果找到了,那么从该区中取一个零散页把数据插进去;否则到表空间中申请一个状态为FREE
的区(也就是空闲的区),把该区的状态变FREE_FRAG
,后从该新申请的区中取一个零散页把数据插进去。之后,在不同的段使用零散页的时候都从该区中取,直到该区中没有空闲页面;然后该区的状态就变成了FULL_FRAG
每个区的XDES Entry
中都记录了该区的状态,这样就可以把相同状态的区串联成一个链表,形成三种链表
FREE
链表FREE_FRAG
链表FULL_FRAG
链表
当段中的数据已经占满32个零散的页后,就直接申请完整的区来插入数据了
XDES Entry
中的Segment ID
用来描述这个区属于哪个段,相同状态的区连接成链表,这样我们就可以快速找到某个段的不同状态的区了
FREE
链表:同一个段中的所有页面都是空闲页面的区NOT_FULL
链表:同一个段中的仍有空闲页面的区FULL
链表:同一个段中的没有空闲页面的区
一个索引对应两个段,每个段都会维护上述3个链表
2、链表基节点
每条链表都有一个List Base Node
结构与之对应,包括3部分
List Length
表明该链表公有多少个节点First Node Page Number
和First Node Offset
表明该链表的头结点在表空间中的位置Last Node Page Number
和Last Node Offset
表明该链表的尾节点在表空间中的位置
9.2.4 段的结构
段由若干个零散的页面以及一些完整的区组成
每个段都有一个INODE Entry
结构与之对应,分为5部分,共192字节
Segment ID
(8字节):段的IDNOT_FULL_N_USED
(4字节):在NOT_FULL
链表中已经使用了多少个页面- 3个
List Base Node
(3X16字节):分别为段的FREE
链表、NOT_FULL
链表、FULL
链表定义的List Base Node
,这样就可以快速找到段对应的不同状态的区 Magic Number
(4字节):标记这个INODE Entry是否已经被初始化Fragment Array Entry
(4X32字节):对应段的32个零散的页面
段是一些零散的页面和一些完整的区的集合
9.2.5 各类型页面的详细情况
1、FSP_HDR
类型
第一个组的第一个区的第一个页面
名称 | 中文名 | 大小(字节) | 描述 |
---|---|---|---|
File Header | 文件头部 | 38 | 页的通用信息 |
File Space Header | 表空间头部 | 112 | 表空间的一些整体属性信息 |
XDES Entry | 区描述信息 | 10240 | 存储本组256个区对应的属性信息 |
Empty Space | 尚未使用的空间 | 5986 | 用于页结构的填充 |
File Trailer | 文件尾部 | 8 | 校验页是否完整 |
2、XDES
类型
之后每个分组的第一个页面只需要记录本组内所有的区对应的XDES Entry
结构即可,不需要再记录表空间的信息,所以XDES
类型和FSP_HDR
类型是一样的,只是没有File Space Header
部分
3、IBUF_BITMAP
类型
每个分组中第二页页面类型都是IBUF_BITMAP
,这种类型的页中记录了一些有关Change Buffer的东西
为了减少插入删除数据带来的随机IO,在修改非唯一二级索引页面时,如果该页面尚未被加载到内存中,那么该修改将先被暂存到Change Buffer中,之后服务器空闲或者其他原因导致对应的页面被加载从内存中时,再将修改合并到对应页面
4、INODE
类型
第一个分组的第三个页面是INODE
类型,INODE
类型的页面是为了存储段的INODE Entry
结构而存在的
名称 | 中文名 | 大小(字节) | 描述 |
---|---|---|---|
File Header | 文件头部 | 38 | 页的通用信息 |
List Node for INODE Page List | 通用链表节点 | 12 | 存储上一个INODE页面和下一个INODE页面的指针 |
INODE Entry | 段描述信息 | 16320 | 存储INODE Entry结构 |
Empty Space | 尚未使用的空间 | 5986 | 用于页结构的填充 |
File Trailer | 文件尾部 | 8 | 校验页是否完整 |
一个页面可以存85个INODE Entry
结构
如果超过了85个,就需要另外的INODE
页面来存储,就需要把这些INODE
页面串联起来,形成两个链表
SEG_INODES_FULL
链表:在该链表中,INODE
类型的页面中已经没有空闲空间来存储额外的INODE Entry
结构SEG_INODE_FREE
链表:在该链表中,INODE
类型的页面中还有空闲空间来存储额外的INODE Entry
结构
存储INODE Entry
的过程大致为:
- 先看看
SEG_INODES_FREE
链表是否为空。如果不为空,直接从该链表中获取一个节点,也就相当于获取到一个仍有空闲空间的INODE
类型的页面,然后把该INODE Entry
结构放到该页面中。当该页面中无剩余空间时,就把该页放到SEG_INODES_FULL
链表中 - 如果
SEG_INODES_FREE
链表为空,则需要从表空间的FREE_FRAG
链表中申请一个页面,并将该页面的类型修改为INODE
,把该页面放到SEG_INODES_FRFE
链表中;与此同时把该INODE Entry
结构放入该页面
9.2.6 Segment Header结构的运用
每个索引的两个段的INODE Entry结构保存在根节点中,在根节点的页面中定义了两个字段
名称(PAGE_) | 大小(字节) | 描述 |
---|---|---|
BTR_SEG_LEAF | 10 | B+树叶子节点段的头部信息,仅在B+树根页面中定义 |
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 | INODE Entry结构在该页面中的偏移量 |
这样索引和对应的段的关系就建立起来了