在传递的参数中,有个hint page no,通常是当前需要分裂的page no的前一个(direction = FSP_DOWN)或者后一个page no(direction = FSP_UP),其目的是将逻辑上相邻的节点在物理上也尽量相邻。
在Step 1我们已经保证了物理空间有足够的数据页,只是还没进行初始化。将page分配到当前segment的流程如下(fseg_alloc_free_page_low):
计算当前segment使用的和占用的page数
使用的page数存储包括FSEG_NOT_FULL链表上使用的page数(存储在inode entry的FSEG_NOT_FULL_N_USED中) + 已用满segment的FSEG_FULL链表上page数 + 占用的frag array page数量;
占用的page数包括FSEG_FREE、FSEG_NOT_FULL、FSEG_FULL三个链表上的Extent + 占用的frag array page数量。
根据hint page获取对应的xdes entry (xdes_get_descriptor_with_space_hdr)
当满足如下条件时该hint page可以直接拿走使用:
Extent状态为XDES_FSEG,表示属于一个segment
hint page所在的Extent已被分配给当前segment(检查xdes entry的XDES_ID)
hint page对应的bit设置为free,表示尚未被占用
返回hint page
当满足条件:1) xdes entry当前是空闲状态(XDES_FREE);2) 该segment中已使用的page数大于其占用的page数的7/8 (FSEG_FILLFACTOR);3) 当前segment已经使用了超过32个frag page,即表示其inode中的frag array可能已经用满。
从表空间分配hint page所在的Extent (fsp_alloc_free_extent),将其从FSP_FREE链表上移除
设置该Extent的状态为XDES_FSEG,写入seg id,并加入到当前segment的FSEG_FREE链表中。
返回hint page
当如下条件时:1) direction != FSP_NO_DIR,对于Btree分裂,要么FSP_UP,要么FSP_DOWN;2)已使用的空间小于已占用空间的7/8; 3)当前segment已经使用了超过32个frag page
尝试从segment获取一个Extent(fseg_alloc_free_extent),如果该segment的FSEG_FREE链表为空,则需要从表空间分配(fsp_alloc_free_extent)一个Extent,并加入到当前segment的FSEG_FREE链表上
direction为FSP_DOWN时,返回该Extent最后一个page,为FSP_UP时,返回该Extent的第一个Page
xdes entry属于当前segment且未被用满,从其中取一个空闲page并返回
如果该segment占用的page数大于实用的page数,说明该segment还有空闲的page,则依次先看FSEG_NOT_FULL链表上是否有未满的Extent,如果没有,再看FSEG_FREE链表上是否有完全空闲的Extent。从其中取一个空闲Page并返回
当前已经实用的Page数小于32个page时,则分配独立的page(fsp_alloc_free_page)并加入到该inode的frag array page数组中,然后返回该block
当上述情况都不满足时,直接分配一个Extent(fseg_alloc_free_extent),并从其中取一个page返回。
上述流程看起来比较复杂,但可以总结为:
对于一个新的segment,总是优先填满32个frag page数组,之后才会为其分配完整的Extent,可以利用碎片页,并避免小表占用太多空间。
尽量获得hint page;
如果segment上未使用的page太多,则尽量利用segment上的page。
上文提到两处从表空间为segment分配数据页,一个是分配单独的数据页,一个是分配整个Extent
表空间单独数据页的分配调用函数fsp_alloc_free_page:
如果hint page所在的Extent在链表XDES_FREE_FRAG上,可以直接使用;否则从根据头page的FSP_FREE_FRAG链表查看是否有可用的Extent;
未能从上述找到一个可用Extent,直接分配一个Extent,并加入到FSP_FREE_FRAG链表中;
从获得的Extent中找到描述为空闲(XDES_FREE_BIT)的page。
分配该page (fsp_alloc_from_free_frag)
设置page对应的bitmap的XDES_FREE_BIT为false,表示被占用;
递增头page的FSP_FRAG_N_USED字段;
如果该Extent被用满了,就将其从FSP_FREE_FRAG移除,并加入到FSP_FULL_FRAG链表中。同时对头Page的FSP_FRAG_N_USED递减1个Extent(FSP_FRAG_N_USED只存储未满的Extent使用的page数量);
对Page内容进行初始化(fsp_page_create)。
表空间Extent的分配函数fsp_alloc_free_extent:
通常先通过头page看FSP_FREE链表上是否有空闲的Extent,如果没有的话,则将新的Extent(例如上述step 1对文件做扩展产生的新page,从FSP_FREE_LIMIT算起)加入到FSP_FREE链表上(fsp_fill_free_list):
一次最多加4个Extent(FSP_FREE_ADD);
如果涉及到xdes page,还需要对xdes page进行初始化;
如果Extent中存在类似xdes page这样的系统管理页,这个Extent被加入到FSP_FREE_FRAG链表中而不是FSP_FREE链表;
取链表上第一个Extent为当前使用;
将获得的Extent从FSP_FREE移除,并返回对应的xdes entry(xdes_lst_get_descriptor)。
回收Page
数据页的回收分为两种,一种是整个Extent的回收,一种是碎片页的回收。在删除索引页或者drop索引时都会发生。