PostgreSQL的FSM分析记录

        近来由于工作原因对PG的FSM(Free Space Map,空闲空间映射表)源码进行了学习。下面给大家简单讲述一下。

        什么是FSM呢,这不得不说一下PG的存储机制了。PG的更新(更新是删除和插入的结合)和删除都是将元组(数据库对我们插入的每一行数据封装后称为元组)标记为无效,而后通过VACUUM进行物理删除。无效的元组被删除后,若是不利用那么会造成存储的浪费,但是如果遍历一遍数据库文件块(Page),以此来找到合适的空闲空间,则会造成比较大的开销。所以,空闲空间映射表FSM就应运而生了,是用来记录每一个文件块剩余的空间。

        这里要注意的是,为了减少对FSM文件I/O的开销,空闲值不是以字节为单位的,而是8字节为单位的,进行了有损压缩。如下所示:

10 0~31 21 32~63 32 64~95 4…… 5255 8159~8191

        下面来介绍一下FSM文件的结构,如下图所示:

        要对其分析,应该先从最下层进行分析,第三层才是对真是文件块空闲空间的记录,而第一层的0号块以及第二层都是为了快速定位合适空间块所产生的辅助块。

        这里的所谓0、1、2、3号块以及辅助块都是FSM的文件块,文件块的结构定义为:

1typedef struct 2{ 3 int fp_next_slot; 4 uint8 fp_nodes[1]; 5} FSMPageData;

        在内存中表现为:

        看起来这没什么,下面我来说一下数据库如何将之便为一棵树来进行遍历寻找空间空间块的。最开始的时候,PG仅仅利用FSM去记录每一个块的空闲值,这样其实效率还是比较低,后来采用了二叉树结构。这里盗用一下Bean_lee的说法:总领袖招来了4000个下属,问,你们谁的树下有超过20个freespace啊。我的4000个下属分别向自己的4000个下属询问,谁的下属有超过20的freespace,下属再向下属的下属询问...,这个场面很鸡飞狗跳,原因在于,没有管理好下属,不了解下属的情况。 要想明白怎么形成树形结构的,首先要写几个算式:

1#define leftchild(x)(2 * (x) + 1)//父亲节点指向左子节点 2#define rightchild(x)(2 * (x) + 2)//父亲节点指向右子节点 3#define parentof(x)(((x) - 1) / 2)//子节点指向父亲节点

        现在画出二叉树的示意图,数字为数组的序号:

                                                                             0

                                                   /                                                      \
                                                  1                                                        2
                                    /                          \                             /                           \
                                   3                           4                           5                             6
                             /           \              /            \               /             \               /             \
                            7            8            9            10            11            12            13            14

                            ……

                    4095             4096             4097     4098             ……                                         8191

        (太难画了……)

        这样大家就可以看到上面算式如何把数组fp_nodes作为树搭建起来了,数组说完了,还有一个fp_next_slot这是个什么呢,我通过printf查看数据库行为,发现这个值是我们得到值是空闲节点数+1,还有就是只有当空闲文件块使用完毕后才会改变其值,他是在当前块空闲值为0后,去查询下一个块,还有就是防止同一个表块的竞争。还有一个比较有意思的循环,我在这里描述一下:同一层的最后一个节点的右节点指向该层的第一个节点。算式如下:

1static int 2rightneighbor(int x) 3{ 4        x++; 5        if (((x + 1) & x) == 0) 6        x = parentof(x); 7        return x; 8} 9 10nodeno = parentof(rightneighbor(nodeno));

        当当前节点是6(括号表示二进制110)时,如何找到该层的第一个呢,6的右子节点是6*2+2 = 14,14+1 = 15(1111) ,15+1 = 16(10000),10000 & 1111 = 0,x = (15-1)/2, x = 7,(7-1)/ 2 = 3。这样就能找到同层的第一个节点。这里便还有许多公式,具体都在src/backend/storage/freespace/fsmpage.c内。 

        其次数据库为了方便查找FSM文件,使用了以下数据结构来表示FSM块在树中的位置。

1typedef struct   2{   3 int  level;  //第一层为2,第三层为0,第二层为1 4 Int logpageno;  //该层的位置 5} FSMAddress;

        对于FSM文件内的逻辑结构,现在已经比较明了了,但是这是怎么去查找一个空闲块的呢?文字叙述肯定难以理解,下面就数据库给出的例子,进行解读。

        例如, 空闲块值树如下,fp_next_slot为(4,7)(fp_next_slot是int,这里为了看起来方便),找寻一个空闲值为6的空闲块:

            7
            7   6
            5 7 6 5
            4 5 5 7 2 6 5 2 

        第一步:找到根节点,为7(0,0),有足够的空闲值,去寻找;
        第二步:查看第四层第三个空闲块,空闲值为5(4,7),不满足,找寻父节点;
        第三步:父节点为5(3,4),也不符合;
        第四步:找到父节点7(2,1)可以,找寻右子节点7(3,2),找寻子节点(4,4)符合;
        第五步:返回 slot(4-1= 3),设置fp_next_slot = slot +1 ;(3+1 = 4).

        以上就是说如何去找的。
        对于数据库对FSM的调整,不是及时的,首先在缓存中进行修改,而后再刷入到磁盘中。

        以上就是对FSM文件的分析记录。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

swap空间的增减方法

(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap