PostgreSQL的FSM分析记录

Stella981
• 阅读 419

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

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

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

0       0~31
1       32~63
2       64~95
……
255     8159~8191

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

PostgreSQL的FSM分析记录

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

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

typedef struct
{
    int        fp_next_slot;
    uint8        fp_nodes[1];
} FSMPageData;

        在内存中表现为:

PostgreSQL的FSM分析记录

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

#define leftchild(x)(2 * (x) + 1)//父亲节点指向左子节点
#define rightchild(x)(2 * (x) + 2)//父亲节点指向右子节点
#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后,去查询下一个块,还有就是防止同一个表块的竞争。还有一个比较有意思的循环,我在这里描述一下:同一层的最后一个节点的右节点指向该层的第一个节点。算式如下:

static int
rightneighbor(int x)
{
        x++;
        if (((x + 1) & x) == 0)
        x = parentof(x);
        return x;
} 

nodeno = 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块在树中的位置。

typedef struct  
{  
    int  level;  //第一层为2,第三层为0,第二层为1
    Int  logpageno;  //该层的位置
}  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文件的分析记录。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
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
Easter79 Easter79
2年前
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
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这