页面类型
| 类型名称(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结构在该页面中的偏移量 |
这样索引和对应的段的关系就建立起来了