重庆思庄Oracle、KingBase、PostgreSQL、Redhat认证学习论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 522|回复: 0
打印 上一主题 下一主题

PostgreSQL数据库扩展pg_visibility详解

[复制链接]
跳转到指定楼层
楼主
发表于 2025-8-17 20:21:30 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
pg_visibility是一个PostgreSQL数据库插件,提供了一种方式来检查一个表的可见性映射(VM)以及页级别的可见性信息。

插件如何使用?
该插件提供了一些函数,通过调用这些函数,可以获取给定关系的可见性信息。用法示例如下:

-- 安装插件
postgres=# create extension pg_visibility;
CREATE EXTENSION

-- 为给定关系的给定块返回其在可见性映射中的“全部可见”和“全部冻结”位。
postgres=# select * from pg_visibility_map('t3',0);
all_visible | all_frozen
-------------+------------
t           | f
(1 row)

-- 为给定关系的给定块返回其在可见性映射中的“全部可见”和“全部冻结”位,外加块的PD_ALL_VISIBLE位。
postgres=# select * from pg_visibility('t3',0);
all_visible | all_frozen | pd_all_visible
-------------+------------+----------------
t           | f          | t
(1 row)

-- 为给定关系的每一块返回其在可见性映射中的“全部可见”和“全部冻结”位,外加每一块的PD_ALL_VISIBLE位。
postgres=# select * from pg_visibility('t2');
blkno | all_visible | all_frozen | pd_all_visible
-------+-------------+------------+----------------
     0 | t           | t          | t
     1 | t           | t          | t
     2 | t           | t          | t
     3 | t           | t          | t
     4 | t           | t          | t
     5 | t           | t          | t
     6 | t           | t          | t
     7 | t           | t          | t
     8 | t           | f          | t
(9 rows)
更多用法可参考pg_visibility

其核心原理就是读取可见性文件以及页面数据,通过位运算来获取页面的可见性信息。页头部有个pd_flags字段,其中包含PD_ALL_VISIBLE位,如果该位为1,则表示该页面所有元组可见。

// 判断页面是否全部可见
#define PageIsAllVisible(page) \
        (((PageHeader) (page))->pd_flags & PD_ALL_VISIBLE)

#define PD_HAS_FREE_LINES        0x0001        /* are there any unused line pointers? */
#define PD_PAGE_FULL                0x0002        /* not enough free space for new tuple? */
#define PD_ALL_VISIBLE                0x0004        /* all tuples on page are visible to everyone */

#define PD_VALID_FLAG_BITS        0x0007        /* OR of all valid pd_flags bits */
函数调用栈:

pg_visibility.so!collect_visibility_data(Oid relid, _Bool include_pd) (pg_visibility\pg_visibility.c:505)
pg_visibility.so!pg_visibility_rel(FunctionCallInfo fcinfo) (pg_visibility\pg_visibility.c:225)
核心函数实现,获取当前表有多少个页,然后查找VM块文件,读取可见性信息,如果需要获取页可见性信息,则读取页面数据,获取pd_flags字段,判断是否全部可见。

static vbits *collect_visibility_data(Oid relid, bool include_pd)
{
        Relation        rel;
        BlockNumber nblocks;
        vbits           *info;
        BlockNumber blkno;
        Buffer                vmbuffer = InvalidBuffer;
        BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD);

        rel = relation_open(relid, AccessShareLock);

        /* Only some relkinds have a visibility map */
        check_relation_relkind(rel);

        nblocks = RelationGetNumberOfBlocks(rel);  // 获取表有多少个页
        info = palloc0(offsetof(vbits, bits) + nblocks);
        info->next = 0;
        info->count = nblocks;

        for (blkno = 0; blkno < nblocks; ++blkno)        // 遍历所有页号
        {
                int32                mapbits;

                /* Make sure we are interruptible. */
                CHECK_FOR_INTERRUPTS();

                // 获取指定表页号的位图信息
                mapbits = (int32) visibilitymap_get_status(rel, blkno, &vmbuffer);
                if ((mapbits & VISIBILITYMAP_ALL_VISIBLE) != 0)
                        info->bits[blkno] |= (1 << 0);
                if ((mapbits & VISIBILITYMAP_ALL_FROZEN) != 0)
                        info->bits[blkno] |= (1 << 1);

                // 是否查看页面可见性信息
                if (include_pd)
                {
                        Buffer                buffer;
                        Page                page;
                        // 读页到buffer
                        buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);
                        LockBuffer(buffer, BUFFER_LOCK_SHARE);

                        page = BufferGetPage(buffer);
                        if (PageIsAllVisible(page))        // 判断页面是否全部可见
                                info->bits[blkno] |= (1 << 2);

                        UnlockReleaseBuffer(buffer);
                }
        }

        /* Clean up. */
        if (vmbuffer != InvalidBuffer)
                ReleaseBuffer(vmbuffer);
        relation_close(rel, AccessShareLock);

        return info;
}
为啥需要可见性映射文件?
我们知道,PostgreSQL中因为其MVCC设计,需要通过vacuum来清理死元组,而vacuum操作会扫描整个表,如果一个页面中所有元组都是可见的,那么就不需要对其进行vacuum操作,为了提高效率,我们对每个页面进行标识,如果这个页没有无效元组,则可以跳过该页面,从而提高vacuum的效率,这就是可见性映射文件的作用。

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 支持支持 反对反对
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|手机版|小黑屋|重庆思庄Oracle、Redhat认证学习论坛 ( 渝ICP备12004239号-4 )

GMT+8, 2026-4-17 21:20 , Processed in 0.237595 second(s), 23 queries .

重庆思庄学习中心论坛-重庆思庄科技有限公司论坛

© 2001-2020

快速回复 返回顶部 返回列表