暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

PostgreSQL插件之pg_freespacemap

原创 chirpyli 2025-02-28
178

PostgreSQL数据库提供了丰富的插件,很多插件可以用来分析数据库的运行情况,pg_freespacemap插件就是其中一个,它可以用来分析数据库中每个表的空闲空间情况,从而帮助DBA了解数据库的运行情况,从而进行优化。

pg_freespacemap插件的使用

pg_freespacemap插件的使用非常简单,只需要在数据库中执行以下命令即可:

-- 安装插件 postgres=# create extension pg_freespacemap; CREATE EXTENSION -- 查看当前安装了哪些插件 postgres=# \dx List of installed extensions Name | Version | Schema | Description -----------------+---------+------------+------------------------------------------------------- pageinspect | 1.9 | public | inspect the contents of database pages at a low level pg_buffercache | 1.3 | public | examine the shared buffer cache pg_freespacemap | 1.2 | public | examine the free space map (FSM) plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language (4 rows)

通过函数pg_freespace获取指定表的空闲空间情况:

-- 查询表t2的空闲空间情况 postgres=# select * from pg_freespace('t2'); blkno | avail -------+------- 0 | 288 1 | 0 2 | 0 3 | 0 4 | 0 5 | 0 6 | 0 7 | 0 8 | 928 (9 rows) -- 查询表t2的块号为0的空闲空间大小 postgres=# select * from pg_freespace('t2', 0); pg_freespace -------------- 288 (1 row)

具体是如何获取的呢?只要读取指定表的FSM文件,在PostgreSQL中,为了提高获取表空闲空间的效率,数据库会为每个表创建一个FSM文件,该文件记录了每个块上的空闲空间大小,通过读取该文件,就可以获取指定表的空闲空间情况。例如,对于表t2,我们查到到其relfilenode的OID,数据库会为其创建一个名为*_fsm的文件,该文件记录了表t2的空闲空间情况。

postgres=# select oid,relname,relfilenode from pg_class where relname='t2'; oid | relname | relfilenode -------+---------+------------- 16438 | t2 | 16842 (1 row)
postgres@slpc:~/psql/data/base/13010$ ls | grep 16842 16842 # heap堆表文件 16842_fsm # FSM文件, 记录每个块上的空闲空间大小 16842_vm # visibility map文件, 加快vacuum的速度

pg_freespacemap插件实现源码分析

这个插件的代码非常简单,就是一个C函数,该函数的作用是获取指定表在指定块上的空闲空间大小,代码如下:

-- Register the C function. CREATE FUNCTION pg_freespace(regclass, bigint) RETURNS int2 AS 'MODULE_PATHNAME', 'pg_freespace' LANGUAGE C STRICT PARALLEL SAFE; -- pg_freespace shows the recorded space avail at each block in a relation CREATE FUNCTION pg_freespace(rel regclass, blkno OUT bigint, avail OUT int2) RETURNS SETOF RECORD AS $$ SELECT blkno, pg_freespace($1, blkno) AS avail FROM generate_series(0, pg_relation_size($1) / current_setting('block_size')::bigint - 1) AS blkno; $$ LANGUAGE SQL PARALLEL SAFE;

其中具体实现的代码如下:

/* * Returns the amount of free space on a given page, according to the * free space map. */ PG_FUNCTION_INFO_V1(pg_freespace); // 入参:表名,块号 // 返回值:空闲空间大小 Datum pg_freespace(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); // 表OID int64 blkno = PG_GETARG_INT64(1); // 块号 int16 freespace; Relation rel; rel = relation_open(relid, AccessShareLock); if (blkno < 0 || blkno > MaxBlockNumber) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid block number"))); freespace = GetRecordedFreeSpace(rel, blkno); // 获取空闲空间大小 relation_close(rel, AccessShareLock); PG_RETURN_INT16(freespace); }

具体是如何获取的呢?下面函数如下:

Size GetRecordedFreeSpace(Relation rel, BlockNumber heapBlk) { FSMAddress addr; uint16 slot; Buffer buf; uint8 cat; /* Get the location of the FSM byte representing the heap block */ addr = fsm_get_location(heapBlk, &slot); // 获取在FSM文件中的位置,即那个块,FSM文件也是8KB大小的块构成 buf = fsm_readbuf(rel, addr, false); // 读FSM文件具体的页到Buffer if (!BufferIsValid(buf)) return 0; cat = fsm_get_avail(BufferGetPage(buf), slot); // 得到具体的值 ReleaseBuffer(buf); // FSM的值明不是实际大小,而是一个映射中的值,需要转换一下 return fsm_space_cat_to_avail(cat); }

至于FSM是怎么设计的?后续文章会继续分析


参考文档:
pg_freespacemap

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论