pg_orphaned扩展分析(一)

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核8GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: pg_orphaned扩展是用于维护PostgreSQL孤儿文件的扩展,通过分析学习了查找孤儿文件的方法,同时还将学习在PostgreSQL后端(backend)如何查找指定表/视图、如何创建cache、如何使用hash表、如何使用List、如何使用正则表达式、C语言扩展如何返回结果集。

pg_orphaned扩展是用于维护PostgreSQL孤儿文件的扩展,通过分析学习了查找孤儿文件的方法,同时还将学习在PostgreSQL后端(backend)如何查找指定表/视图、如何创建cache、如何使用hash表、如何使用List、如何使用正则表达式、C语言扩展如何返回结果集。

有关孤儿文件的内容可参考:


pg_orphaned扩展实现了pg_list_orphanedpg_list_orphaned_movedpg_move_orphanedpg_remove_moved_orphanedpg_move_back_orphaned 五个函数:

CREATE FUNCTION pg_list_orphaned(  older_than interval default null,  OUT dbname text,  OUT path text,  OUT name text,  OUT size bigint,  OUT mod_time timestamptz,  OUT relfilenode bigint,  OUT reloid bigint,  OUT older bool)RETURNS SETOF RECORD
AS'MODULE_PATHNAME','pg_list_orphaned'LANGUAGE C VOLATILE;CREATE FUNCTION pg_list_orphaned_moved(  OUT dbname text,  OUT path text,  OUT name text,  OUT size bigint,  OUT mod_time timestamptz,  OUT relfilenode bigint,  OUT reloid bigint)RETURNS SETOF RECORD
AS'MODULE_PATHNAME','pg_list_orphaned_moved'LANGUAGE C VOLATILE;CREATE FUNCTION pg_move_orphaned(older_than interval default null)    RETURNS int    LANGUAGE c
AS'MODULE_PATHNAME','pg_move_orphaned';CREATE FUNCTION pg_remove_moved_orphaned()    RETURNS void
    LANGUAGE c
AS'MODULE_PATHNAME','pg_remove_moved_orphaned';CREATE FUNCTION pg_move_back_orphaned()    RETURNS int    LANGUAGE c
AS'MODULE_PATHNAME','pg_move_back_orphaned';revoke execute on function pg_list_orphaned(older_than interval)from public;revoke execute on function pg_list_orphaned_moved()from public;revoke execute on function pg_move_orphaned(older_than interval)from public;revoke execute on function pg_remove_moved_orphaned()from public;revoke execute on function pg_move_back_orphaned()from public;

pg_list_orphaned

pg_list_orphaned函数是整个扩展的核心,返回孤儿文件列表。查找孤儿文件的原理是通过遍历pg缺省表空间目录以及PG_DATA/pg_tblspc目录下的文件(仅查找全数字文件、临时表文件),并逐一在pg_class中查找该条目(通过reltablespace和relfilenode),如果pg_class中不存在该条目,则认定该文件是孤儿文件,查找过程通过 search_orphaned 函数实现。核心函数是RelidByRelfilenodeDirty

search_orphaned

/* 忽略文件名非数字的文件 */if (strstr(de->d_name, "_") ==NULL&&isdigit((unsignedchar) *(de->d_name))) {
orph=palloc(sizeof(*orph));
relfilename=strdup(de->d_name);
relfilenode= (Oid) strtoul(relfilename, &relfilename, 10);
/* 如果RelidByRelfilenodeDirty没有返回有效的oid* 我们就认为此文件是个孤儿文件*/oidrel=RelidByRelfilenodeDirty(reltablespace, relfilenode);
/*           * 如果是指定relation的第一个segment文件、大小为0并且是在上一次checkpoint之后创建的文件* 我们过滤之并不做为孤儿文件上报* due to https://github.com/postgres/postgres/blob/REL_12_8/src/backend/storage/smgr/md.c#L225*/segment_time=time_t_to_timestamptz(attrib.st_mtime);
if (!OidIsValid(oidrel) &&!(attrib.st_size==0&&strstr(de->d_name, ".") ==NULL&&segment_time>last_checkpoint_time))
            {
orph->dbname=strdup(dbname);
orph->path=strdup(dir);
orph->name=strdup(de->d_name);
orph->size= (int64) attrib.st_size;
orph->mod_time=segment_time;
orph->relfilenode=relfilenode;
orph->reloid=oidrel;
*flist=lappend(*flist, orph);
/* search for _init and _fsm */if(strstr(de->d_name, ".") ==NULL)
pgorph_add_suffix(flist, orph);
            }       
        }

last_checkpoint_time在pg_build_orphaned_list函数中获取:

/* get a copy of the control file */#if PG_VERSION_NUM >= 120000ControlFile=get_controlfile(".", &crc_ok);
#elseControlFile=get_controlfile(".", NULL, &crc_ok);
#endifif (!crc_ok)
ereport(ERROR,(errmsg("pg_control CRC value is incorrect")));
/* get last checkpoint time */time_tmp= (time_t) ControlFile->checkPointCopy.time;
last_checkpoint_time=time_t_to_timestamptz(time_tmp);

RelidByRelfilenodeDirty

/** Map a relation's (tablespace, filenode) to a relation's oid and cache the* result.** This is the same as the existing RelidByRelfilenode in relfilenodemap.c but* it is done by using a DirtySnapshot as we want to see relation being created.** Returns InvalidOid if no relation matching the criteria could be found.*/OidRelidByRelfilenodeDirty(Oidreltablespace, Oidrelfilenode){...}

RelidByRelfilenodeDirty是核心函数,入口参数reltablespace、relfilenode,分别是表空间oid跟文件oid,首先在cache中查找,如果cache中没有,则去pg_class中找,没找到,则说明是孤儿文件,加入cache中,关键代码如下:

else    {
/** Not a shared table, could either be a plain relation or a* non-shared, nailed one, like e.g. pg_class.*//* check for plain relations by looking in pg_class */#if PG_VERSION_NUM >= 120000/** RelationRelationId在pg_class_d.h中定义* 值为1259,pg_class的OID* 所以这里打开pg_class*/relation=table_open(RelationRelationId, AccessShareLock);
#elserelation=heap_open(RelationRelationId, AccessShareLock);
#endif/* copy scankey to local copy, it will be modified during the scan *//* relfilenode_skey_dirty的解释见下文 */memcpy(skey, relfilenode_skey_dirty, sizeof(skey));
/* 设置扫描参数 */skey[0].sk_argument=ObjectIdGetDatum(reltablespace);
skey[1].sk_argument=ObjectIdGetDatum(relfilenode);
scandesc=systable_beginscan(relation,
ClassTblspcRelfilenodeIndexId,
true,
&DirtySnapshot,
2,
skey);
found=false;
while (HeapTupleIsValid(ntp=systable_getnext(scandesc)))
        {
#if PG_VERSION_NUM >= 120000Form_pg_classclassform= (Form_pg_class) GETSTRUCT(ntp);
found=true;
Assert(classform->reltablespace==reltablespace);
Assert(classform->relfilenode==relfilenode);
relid=classform->oid;
#elsefound=true;
relid=HeapTupleGetOid(ntp);
#endif        }
systable_endscan(scandesc);
#if PG_VERSION_NUM >= 120000table_close(relation, AccessShareLock);
#elseheap_close(relation, AccessShareLock);
#endif/* check for tables that are mapped but not shared */if (!found)
#if PG_VERSION_NUM >= 160000relid=RelationMapFilenumberToOid(relfilenode, false);
#elserelid=RelationMapFilenodeToOid(relfilenode, false);
#endif    }
/** Only enter entry into cache now, our opening of pg_class could have* caused cache invalidations to be executed which would have deleted a* new entry if we had entered it above.*/entry=hash_search(RelfilenodeMapHashDirty, (void*) &key, HASH_ENTER, &found);
if (found)
elog(ERROR, "corrupted hashtable");
entry->relid=relid;

InitializeRelfilenodeMapDirty

relfilenode_skey_dirty是个全局变是,并在InitializeRelfilenodeMapDirty函数中初始化:

/** Initialize cache, either on first use or after a reset.* Same as InitializeRelfilenodeMap in relfilenodemap.c*/staticvoidInitializeRelfilenodeMapDirty(void)
{
HASHCTLctl;
inti;
/* 确保我们已经初始化了CacheMemoryContext. */if (CacheMemoryContext==NULL)
CreateCacheMemoryContext();
/* 构造skey */MemSet(&relfilenode_skey_dirty, 0, sizeof(relfilenode_skey_dirty));
/* 我们搜索pg_class时使用了两个键值,tablespace和filenode,所以这里是2 */for (i=0; i<2; i++)
    {
/* 填充FmgrInfo结构 */fmgr_info_cxt(F_OIDEQ,
&relfilenode_skey_dirty[i].sk_func,
CacheMemoryContext);
/* 使用Btree索引相等策略 */relfilenode_skey_dirty[i].sk_strategy=BTEqualStrategyNumber;
relfilenode_skey_dirty[i].sk_subtype=InvalidOid;
relfilenode_skey_dirty[i].sk_collation=InvalidOid;
    }
/* 设置查找键值 */relfilenode_skey_dirty[0].sk_attno=Anum_pg_class_reltablespace;
relfilenode_skey_dirty[1].sk_attno=Anum_pg_class_relfilenode;
/* 初始化hash表 */MemSet(&ctl, 0, sizeof(ctl));
ctl.keysize=sizeof(RelfilenodeMapKeyDirty);
ctl.entrysize=sizeof(RelfilenodeMapEntryDirty);
/* hash表位于CacheMemoryContext */ctl.hcxt=CacheMemoryContext;
/** Only create the RelfilenodeMapHashDirty now, so we don't end up partially* initialized when fmgr_info_cxt() above ERRORs out with an out of memory* error.* Note that the hash table is not created in shared memory but in* private memory.*/RelfilenodeMapHashDirty=hash_create("RelfilenodeMap cache", 64, &ctl,
HASH_ELEM|HASH_BLOBS|HASH_CONTEXT);
/* Watch for invalidation events. */CacheRegisterRelcacheCallback(RelfilenodeMapInvalidateCallbackDirty,
                                    (Datum) 0);
}

这里hash表用到了RelfilenodeMapKeyDirty、RelfilenodeMapEntryDirty两个结构,分别定义如下:

typedefstruct{
Oidreltablespace;
Oidrelfilenode;
} RelfilenodeMapKeyDirty;
typedefstruct{
RelfilenodeMapKeyDirtykey;          /* lookup key - must be first */Oidrelid;                  /* pg_class.oid */} RelfilenodeMapEntryDirty;

后端hash表的使用

pg_orphaned扩展使用pg后端的hash表实现cache,这里简单总结下如何使用后端提供的hash函数。相关代码在源码的src/backend/utils/hash/dynahash.c中:

HTAB*hash_create(constchar*tabname, longnelem, constHASHCTL*info, intflags);
void*hash_search(HTAB*hashp,
constvoid*keyPtr,
HASHACTIONaction,
bool*foundPtr)

hash_create的flag参数常用的值有:

HASH_ELEM - 必须包含此值

HASH_STRINGSHASH_BLOBSHASH_FUNCTION - 三都必须包含其一

HASH_CONTEXT - 如果包含此值,表示将hash表分配到info->hcxt指定的内存上下文中,缺省分配hash表到TopMemoryContext


hash_search的HASHACTION常用的值有:

*      HASH_FIND: 在表中查找key

*      HASH_ENTER: 在表中查找key,如果key不存在则创建

*      HASH_ENTER_NULL: 同上,如果内存不足返回NULL

*      HASH_REMOVE: 从表中删除指定key


用法见下面的代码片断:

/* Hash table for information about each relfilenode <-> oid pair */staticHTAB*RelfilenodeMapHashDirty=NULL;
HASHCTLctl;
/* 初始化hash表 */MemSet(&ctl, 0, sizeof(ctl));
ctl.keysize=sizeof(RelfilenodeMapKeyDirty);
ctl.entrysize=sizeof(RelfilenodeMapEntryDirty);
/* hash表位于CacheMemoryContext */ctl.hcxt=CacheMemoryContext;
RelfilenodeMapHashDirty=hash_create("RelfilenodeMap cache", 64, &ctl,
HASH_ELEM|HASH_BLOBS|HASH_CONTEXT);
/* hash_search */RelfilenodeMapEntryDirty*entry;
entry=hash_search(RelfilenodeMapHashDirty, (void*) &key, HASH_FIND, &found);



相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
索引
ElasticSearch 报错及解决 详细 0405【已解决】
ElasticSearch 报错及解决 详细 0405【已解决】
1185 4
|
Kubernetes Java Nacos
nacos常见问题之通过helm方式部署设置开启授权认证功能如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
1104 0
|
存储 NoSQL 关系型数据库
PostgreSQL列存扩展hydra简单测试
Hydra是一款PostgreSQL的扩展,为PostgreSQL增加了列存引擎,使得PostgreSQL的olap性能大幅提升,本文介绍Hydra基本的使用方法。
|
关系型数据库 PostgreSQL
PostgreSQL pg_orphaned扩展
由于种种原因,PostgreSQL可能会产生一些孤儿文件,这些文件会占用磁盘空间,手工查找费时费力还容易出错,pg_orphaned扩展很好的解决了这个问题。
|
8月前
|
人工智能 自然语言处理 Rust
【内附榜单】评估AI大模型的代码修复能力!Multi-SWE-bench:字节开源代码修复能力评估基准,覆盖7大主流编程语言
Multi-SWE-bench是首个覆盖Python外7种主流编程语言的代码修复基准,包含1632个真实GitHub问题样本,通过严格筛选与人工验证确保数据质量。
645 0
【内附榜单】评估AI大模型的代码修复能力!Multi-SWE-bench:字节开源代码修复能力评估基准,覆盖7大主流编程语言
|
9月前
|
安全 Unix Linux
VMware Workstation 17.6.3 发布下载,现在完全免费无论个人还是商业用途
VMware Workstation 17.6.3 发布下载,现在完全免费无论个人还是商业用途
70965 65
|
10月前
|
JavaScript 前端开发 API
如何在React.js中使用Shadcn/UI
学习如何在React.js中使用Shadcn/UI构建轻量且可定制的现代化界面。Shadcn/UI为React.js打造,提供核心组件和Tailwind CSS支持,帮助你创建独特的UI,避免大型框架的臃肿。本文介绍安装、配置及与Apipost集成的方法,适合希望提升React.js项目的开发者。通过定制主题和优化性能,你可以高效地开发出功能强大的应用。
|
Ubuntu Linux
编译内核遇到pahole不可用
编译内核遇到pahole不可用
|
存储 关系型数据库 数据库
PostgreSQL孤儿文件
与所有其他关系数据库系统一样,PostgreSQL需要通过写入wal日志或在Checkpoint时同步数据到数据文件来持久化数据到磁盘上。对于数据文件,一旦Relation达到SEGMENT_SIZE(默认1GB),PostgreSQL就会创建一个新的数据文件。因此如果Relation持续增长,则该Relation可能会由多个文件组成。在这篇文章中想要考虑的问题是,是否可能存在孤儿文件。
|
存储 SQL 关系型数据库
StarRocks简介
【5月更文挑战第4天】StarRocks是Linux基金会的开源MPP数据库,提供MySQL协议兼容性,支持标准SQL,用于快速数据分析。它适用于OLAP、实时数仓、高并发查询等场景,具有无外部依赖、高可用和易运维的特点。StarRocks支持多种BI工具,如Tableau,且可构建各种数据模型。其系统架构包括Frontend(FE)和Backend(BE),提供存算一体和存算分离两种模式。此外,StarRocks支持四种表类型和多种数据类型,满足不同业务需求。
2270 0
StarRocks简介

热门文章

最新文章