1 表空间总览
2 页面结构
其中页面的通用部分有:File Header和 File Trailer。
2.1 File Header组成
- check sum:校验和
- page offset:页号
- prev & next:前后指针
- LSN:页面最后修改对应的LSN
- type:页面类型
- undo、溢出页、新分配、索引页(数据页)…
- flush LSN:仅在系统表空间中第一页定义,代表文件至少被刷新到了对应的LSN
- space ID:属于哪个表空间
3 区和组的概念
连续64页为一个区,一个区就是1MB
,256个区为一组
区的提出是想解决,索引页(也就是数据页)双向链表相邻的两个页物理距离可能比较远,这样就导致去磁盘加载页面的时候需要重新定位磁头,导致随机I/O,如果尽量的让页面距离更近,就会把随机IO换成顺序IO,从而提高查询速度。
4 段的概念
有了区的概念之后还没结束,因为不把叶子节点和非叶子节点区分开来扫描结果还是不太理想的。
想象一下B+树形成的过程,插入记录不断有新的页面(叶子节点)产生,同时也产生了很多索引页面(非叶子节点),如果把这些页面统统放到一个区里,那么叶子节点还是存在物理距离的。所以有了段的概念。
叶子节点有自己的区,非叶子节点有自己的区,分别的区集合就是段。
所以一个索引会产生两个段:非叶子节点段 和 叶子节点段。
对于数据量小的表来说,一次分配一整个区还是浪费空间的,因为一个区就是1MB,两个段就是至少两个区,一个小表至少需要使用2MB吗?所以又有了碎片区的概念。
- 该开始向表中插入数据,段是从某个碎片区以单个页面来分配存储空间的
- 当某个段占用了32个碎片区页面之后,则以完整的去为单位来分配存储空间。
所以段的更精准的定义应该是:某些零散的页面和一些完整的区的集合。
5 区详解
5.1 区的四种状态
- 空闲区:没有任何页面被使用(直属表空间)
- 有剩余页面的碎片区(直属表空间)
- 没有剩余页面的碎片区(直属表空间)
- 属于某个段的区
5.2 区的管理:XDES Entry
用上边这个结构体来管理每个区,每个区都有一个自己的结构体!
- Segment ID:所在段的ID
- List Node:每个区的管理结构体穿成链表
- State:四种状态
- Page State Bit Map :16字节,128位,一个区64页,每两个bit对应一个页面,第一位表示页面是否空闲,第二位没使用
5.3 插入数据过程重捋
-
- 当段中数据比较少时,首先去表空间直属的【还有空闲页面的碎片区】申请一个零散页面,如果现在的【碎片区】已满,就去表空间中新申请一个区,然后把这个区变为表空间直属的【碎片区】,此时可以申请到零散页面,从而插入数据
- 早晚有一天,新申请的这个【碎片区】也会没有空闲页面,把它变为【没有空闲页面的碎片区】之后,新申请一个【碎片区】…
-
- 数据比较多的时候,就直接申请一个段所属的区…
5.4 XDES Entry链表
我们怎么知道哪些区的状态是【有可用】哪些状态是【没有可用空闲页】的呢?当数据量足够大的时候,不能每次都遍历所有的XDES Entry。
所以就用到了这个链表结构。
5.4.1 直属表空间的碎片区链表
5.4.2 属于某个段的链表
当某个段占用32个碎片页面之后,就直接申请完整的区了。此时的区不再是表空间直属的了。所以还需要使用链表管理只属于某个区的链表。
链表形式跟上边一样。
每个索引B+树有两个段,每个段还有三个链表。
5.4.3 链表基地址管理
每个链表对应一个链表基节点(List Base Node)
6 段的管理
6.1 INODE Entry
段的精准的定义是:32个零散的页面和一些完整的区的集合。
每个段都定义了一个INODE Entry的数据结构来管理段。
7 FSP_HDR页面类型
7.1 第一组第一个页面
表空间的第一个页面
存储了表空间一些整体属性和第一个组内的256个区对应的XDES Entry结构
8 XDES页面类型
8.1 组的第一个页面
9 IBUF_BITMAP页面类型
9.1 Change Buffer相关
每插入一条记录其实都是想B+树中插入记录,插入过程是先把这个记录插入到聚簇索引的页面中,然后插入到二级索引页面中。
虽然每个索引中断都是距离尽可能进的,但是这些段属于不同的索引,所以这些页面在表空间中仍然是随机分布,仍然会产生随机I/O,严重影响性能。
所以有了Change Buffer。其实他本质上也是一个B+树。
9.2 Change Buffer作用
当页面仍然在磁盘上时,那么该修改将先被暂存在Change Buffer中,之后服务器空闲,或者页面从磁盘载入内存中时,再将其修改合并到对应页面。
10 INODE类型页面
10.1 第一分组中第三个页面
当页面中的段超过85个,也就是INODE Entry超过85个时,一个页就存不下了。就需要使用额外的INODE类型页面存储这些结构。
10.2 存储INODE Entry
- 先看看Free链表是否为空,如果不为空,则取出第一个链表节点,放入该页面。如果接入这个Entry之后页面正好满了,就把这个页面放入FULL链表。
- 如果链表为空,就需要从表空间中申请一个页面,页面类型修改为INODE类型,放入FREE链表,把Entry放入新申请的页面中。