pg_orphaned扩展分析(二)

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

后端系统表扫描

pg_orphaned扩展通过在pg_class表中查找reltablespace和relfilenode以确定指定文件是否为孤儿文件,pg_orphaned是如何做呢?首先使用 InitDirtySnapshot 宏初始化一个类型为 SNAPSHOT_DIRTY SnapshotData结构,然后使用 open_table(>=pg12)或者tuple_open 打开表,最后使用 systable_beginscan 扫描,参考下面的代码片断:

SnapshotDataDirtySnapshot;
ScanKeyDataskey[2];
InitDirtySnapshot(DirtySnapshot);
......
#if PG_VERSION_NUM >= 120000relation=table_open(RelationRelationId, AccessShareLock);
#elserelation=heap_open(RelationRelationId, AccessShareLock);
#endif/* copy scankey to local copy, it will be modified during the scan */memcpy(skey, relfilenode_skey_dirty, sizeof(skey));
/* set scan arguments */skey[0].sk_argument=ObjectIdGetDatum(reltablespace);
skey[1].sk_argument=ObjectIdGetDatum(relfilenode);
scandesc=systable_beginscan(relation,
ClassTblspcRelfilenodeIndexId,
true,
&DirtySnapshot,
2,
skey);

SnapshotData 结构见 include/server/utils/snapshot.h。

open_table的实现在src/backend/access/table/table.c中,原型如下:

Relationtable_open(OidrelationId, LOCKMODElockmode)

参数relationId为要打开的表的oid,lockmode为加锁模式,定义如下:

/* NoLock is not a lock mode, but a flag value meaning "don't get a lock" */#define NoLock                  0#define AccessShareLock         1   /* SELECT */#define RowShareLock            2   /* SELECT FOR UPDATE/FOR SHARE */#define RowExclusiveLock        3   /* INSERT, UPDATE, DELETE */#define ShareUpdateExclusiveLock 4  /* VACUUM (non-FULL),ANALYZE, CREATE INDEX* CONCURRENTLY */#define ShareLock               5   /* CREATE INDEX (WITHOUT CONCURRENTLY) */#define ShareRowExclusiveLock   6   /* like EXCLUSIVE MODE, but allows ROW* SHARE */#define ExclusiveLock           7   /* blocks ROW SHARE/SELECT...FOR UPDATE */#define AccessExclusiveLock     8   /* ALTER TABLE, DROP TABLE, VACUUM FULL,* and unqualified LOCK TABLE */#define MaxLockMode             8   /* highest standard lock mode */

systable_beginscan 实现于src/backend/access/index/genam.c,用于对指定系统表进行heap-or-index扫描,共有六个参数,原型如下:

SysScanDescsystable_beginscan(RelationheapRelation,
OidindexId,
boolindexOK,
Snapshotsnapshot,
intnkeys, ScanKeykey)

共有六个参数:

heapRelation: 要扫描的oid,已经打开并加了合适的锁;

indexId: 查找条件的索引id;

indexOk: false强制heap scan;

snapshot: 为NULL时使用最近的catalog快照;

nkeys, key: 扫描使用的键值。

返回 SysScanDesc结构,定义如下:

/* Struct for storage-or-index scans of system tables */typedefstructSysScanDescData{
Relationheap_rel;       /* catalog being scanned */Relationirel;           /* NULL if doing heap scan */structTableScanDescData*scan; /* only valid in storage-scan case */structIndexScanDescData*iscan;    /* only valid in index-scan case */structSnapshotData*snapshot;  /* snapshot to unregister at end of scan */structTupleTableSlot*slot;
} SysScanDescData;

扫描后结果获取:

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 关闭扫描并释放资源,然后调用 close_table 关闭表。

systable_endscan(scandesc);
#if PG_VERSION_NUM >= 120000table_close(relation, AccessShareLock);
#elseheap_close(relation, AccessShareLock);
#endif

使用后端List

PostgreSQL后端实现了List包,位于src/backend/nodes/list.c。pg_orphaned扩展使用List保存孤独文件列表。

List*list_orphaned_relations=NULL;
typedefstructOrphanedRelation {
char*dbname;
char*path;
char*name;
intsize;
TimestampTzmod_time;
Oidrelfilenode;
Oidreloid;
} OrphanedRelation;

主要操作如下:

//新增oidrel=RelidByRelfilenodeDirty(reltablespace, relfilenode);
if (!OidIsValid(oidrel)) {
orph->dbname=strdup(dbname);
orph->path=strdup(dir);
orph->name=strdup(de->d_name);
orph->size= (int64) attrib.st_size;
orph->mod_time=time_t_to_timestamptz(attrib.st_mtime);
orph->relfilenode=relfilenode;
orph->reloid=oidrel;
*flist=lappend(*flist, orph);                             
}
......
//遍历#if (PG_VERSION_NUM < 130000)for (cell=list_head(list_orphaned_relations); cell!=NULL; cell=lnext(cell))
#elsefor (cell=list_head(list_orphaned_relations); cell!=NULL; cell=lnext(list_orphaned_relations, cell))
#endif    {
charorphaned_file[MAXPGPATH+21+sizeof(TABLESPACE_VERSION_DIRECTORY) +10+6] = {0};
charorphaned_file_backup_dir[MAXPGPATH+21+sizeof(TABLESPACE_VERSION_DIRECTORY) +10+6] = {0};
charorphaned_file_backup[MAXPGPATH+21+sizeof(TABLESPACE_VERSION_DIRECTORY) +10+6] = {0};
OrphanedRelation*orph= (OrphanedRelation*)lfirst(cell);

C语言自定义函数如何返回结果集

根据fmgr的定义,如果返回的是结果集而不是简单类型,需要使用指向ReturnSetInfo类型的节点的fcinfo->resultinfo调用该函数,该结构会由调用者初始化并传递给要调用的函数,参考代码如下(节选自pg_list_orphaned_internal):

ReturnSetInfo*rsinfo= (ReturnSetInfo*) fcinfo->resultinfo;
Tuplestorestate*tupstore;
TupleDesctupdesc;
MemoryContextper_query_ctx;
MemoryContextoldcontext;
ListCell*cell;
per_query_ctx=rsinfo->econtext->ecxt_per_query_memory;
oldcontext=MemoryContextSwitchTo(per_query_ctx);
/** 为我们的返回类型构造元组描述符*/if (get_call_result_type(fcinfo, NULL, &tupdesc) !=TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");
/* 在work_mem中创建tuplestore */tupstore=tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode=SFRM_Materialize;
rsinfo->setResult=tupstore;
rsinfo->setDesc=tupdesc;
MemoryContextSwitchTo(oldcontext);
for(...)
{
/* 获取数据 */OrphanedRelation*orph= (OrphanedRelation*)lfirst(cell);
/* 填充返回数据 */Datumvalues[8];
boolnulls[8];
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
values[0] =CStringGetTextDatum(orph->dbname);
values[1] =CStringGetTextDatum(orph->path);
values[2] =CStringGetTextDatum(orph->name);
values[3] =Int64GetDatum(orph->size);
values[4] =TimestampTzGetDatum(orph->mod_time);
values[5] =Int64GetDatum(orph->relfilenode);
values[6] =Int64GetDatum(orph->reloid);
if (orph->mod_time<=limitts)
values[7] =BoolGetDatum(true);
elsevalues[7] =BoolGetDatum(false);
/* 结果放入tuplestore中 */tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
/* 清理 */tuplestore_donestoring(tupstore);

正则表达式

正则表达式需要使用宽字符集,实现位于src/backend/utils/adt/regexp.c、/src/backend/regex/regcomp.c、/src/backend/regex/regexec.c,使用起来相对简单,见如下示例:

} elseif (de->d_name[0] =='t') {
inti;
pg_wchar*wstr;
intwlen;
pg_wchar*regwstr;
intregwlen;
intr;
char*regex="^t[0-9]*_[0-9]";
intregcomp_result;
charerrMsg[100];
regex_t*preg=palloc(sizeof(regex_t));
char*t;
char*tokptr=NULL;
char*temprel;
regwstr=palloc((strlen(regex) +1) *sizeof(pg_wchar));
regwlen=pg_mb2wchar_with_len(regex, regwstr, strlen(regex));
/* 编译正则表达式 */regcomp_result=pg_regcomp(preg,
regwstr,
regwlen,
REG_ADVANCED|REG_NOSUB,
DEFAULT_COLLATION_OID);
pfree(regwstr);
if (regcomp_result==REG_OKAY) {
wstr=palloc((strlen(de->d_name) +1) *sizeof(pg_wchar));
wlen=pg_mb2wchar_with_len(de->d_name, wstr, strlen(de->d_name));
/* 匹配正则表达式 */r=pg_regexec(preg, wstr, wlen, 0, NULL, 0, NULL, 0);
if (r!=REG_NOMATCH) {
            ......
相关文章
|
关系型数据库 Go PostgreSQL
golang pgx自定义PostgreSQL类型
golang的pgx驱动提供了大约70种PostgreSQL类型支持,但还是有一些类型没有涵盖,本文介绍如何自己编写代码支持特殊的类型。
|
Prometheus 负载均衡 Cloud Native
OceanBase数据库常见问题之默认情况下流量分布还是集中在一个zone上如何解决
OceanBase 是一款由阿里巴巴集团研发的企业级分布式关系型数据库,它具有高可用、高性能、可水平扩展等特点。以下是OceanBase 数据库使用过程中可能遇到的一些常见问题及其解答的汇总,以帮助用户更好地理解和使用这款数据库产品。
|
关系型数据库 PostgreSQL
PostgreSQL pg_orphaned扩展
由于种种原因,PostgreSQL可能会产生一些孤儿文件,这些文件会占用磁盘空间,手工查找费时费力还容易出错,pg_orphaned扩展很好的解决了这个问题。
|
前端开发 JavaScript API
取网页纯文本内容免费API接口教程
该API用于获取指定网页的纯文本内容,去除HTML标签、CSS和JS等元素。支持POST和GET请求,需提供ID、Key、URL等参数。请求示例:https://cn.apihz.cn/api/wangzhan/getyuan.php?id=88888888&key=88888888&url=www.apihz.cn&dy=1。返回纯文本数据。
352 1
|
应用服务中间件 Linux nginx
Docker镜像-手动制作yum版nginx镜像
这篇文章介绍了如何手动制作一个基于CentOS 7.6的Docker镜像,其中包括下载指定版本的CentOS镜像,创建容器,配置阿里云软件源,安装并配置nginx,自定义nginx日志格式和web页面,最后提交镜像并基于该镜像启动新容器的详细步骤。
386 21
Docker镜像-手动制作yum版nginx镜像
|
应用服务中间件 Linux nginx
Docker镜像-基于DockerFile制作yum版nginx镜像
本文介绍了如何使用Dockerfile制作一个基于CentOS 7.6.1810的yum版nginx镜像,并提供了详细的步骤和命令。
365 20
|
人工智能 弹性计算 定位技术
【云故事探索】NO.4: 千寻位置,时空智能赋能行业数字化转型
千寻位置,成立于2015年,利用北斗卫星系统及全球5000多座增强站,提供厘米级定位服务。该公司借助阿里云的计算能力,为汽车、农业等多个行业提供高精度时空智能解决方案,推动行业转型升级。千寻已完成超130亿元估值的A轮融资,展现了其在时空智能领域的领先地位。通过云上部署,千寻优化服务质量和市场扩展,应对突发流量,计划进一步全球化并应用AI技术。阿里云的支持对于千寻的成功至关重要,双方合作将时空智能服务推向国际。
【云故事探索】NO.4: 千寻位置,时空智能赋能行业数字化转型
|
10月前
快速拥有满血全功能DeepSeek,再也不怕服务器繁忙!
官网繁忙不用怕!现在跟我一起通过视频学习阿里云《零门槛、即刻拥有 DeepSeek-R1 满血版》技术解决方案,最快两步三分钟就能拥有 免费!满血版!随时可用!支持联网搜索!带有个人知识库(RAG)!的DeepSeek 模型能力!!!
347 0
快速拥有满血全功能DeepSeek,再也不怕服务器繁忙!
|
存储 关系型数据库 数据库
PostgreSQL孤儿文件
与所有其他关系数据库系统一样,PostgreSQL需要通过写入wal日志或在Checkpoint时同步数据到数据文件来持久化数据到磁盘上。对于数据文件,一旦Relation达到SEGMENT_SIZE(默认1GB),PostgreSQL就会创建一个新的数据文件。因此如果Relation持续增长,则该Relation可能会由多个文件组成。在这篇文章中想要考虑的问题是,是否可能存在孤儿文件。
|
Apache 数据安全/隐私保护
HAProxy的高级配置选项-ACL篇之域名重定向案例
这篇文章介绍了HAProxy的ACL(访问控制列表)功能,特别是如何基于域名进行重定向的高级配置选项,并通过实战案例展示了如何配置ACL规则以实现基于特定域名的HTTP重定向。
268 6
HAProxy的高级配置选项-ACL篇之域名重定向案例