解析PostgreSQL中Oid和Relfilenode的映射问题

作者李传成
中国PG分会认证专家,瀚高软件资深内核研发工程师
https://zhuanlan.zhihu.com/p/342466054

PostgreSQL中的表会有一个RelFileNode值指定这个表在磁盘上的文件名(外部表、分区表除外)。一般情况下在pg_class表的relfilenode字段可以查出这个值,但是有一些特定表在relfilenode字段的查询结果是0,这个博客中将会探究这些特殊表relfilenode的内核处理。

正常表的Relfilenode

当我们创建一张普通表时,在pg_class系统表里可以查询出其relfilenode,可以看出在表刚刚创建时其oid和relfilenode都是16808,在磁盘上也可以查询到16808这个文件。事实上,这个文件存储了我们向表t2插入的数据。

postgres=# create table t2(i int);
CREATE TABLE
postgres=# select oid,relname,relfilenode from pg_class where relname = 't2';
 oid | relname | relfilenode
-------+---------+-------------
 16808 | t2   |    16808
(1 row)

postgres=# \q
movead@movead-PC:/h2/pgpgpg/bin$ ll ../data/base/12835/16808
-rw-------+ 1 movead movead 0 12月 31 17:11 ../data/base/12835/16808
movead@movead-PC:/h2/pgpgpg/bin$

在我们对一张表执行truncate,vacuum full等操作后,会重写这个表的数据,会引发这个表relfilenode值的变更。如下测试可以看出truncate之后,t2表的relfilenode从16808变为了16811.

postgres=# truncate t2;
TRUNCATE TABLE
postgres=# select oid,relname,relfilenode from pg_class where relname = 't2';
 oid | relname | relfilenode
-------+---------+-------------
 16808 | t2   |    16811
(1 row)

postgres=# checkpoint;
CHECKPOINT
postgres=# \q
movead@movead-PC:/h2/pgpgpg/bin$ ll ../data/base/12835/16808
ls: 无法访问'../data/base/12835/16808': 没有那个文件或目录
movead@movead-PC:/h2/pgpgpg/bin$ ll ../data/base/12835/16811
-rw-------+ 1 movead movead 0 12月 31 17:16 ../data/base/12835/16811
movead@movead-PC:/h2/pgpgpg/bin$

Nail表的Relfilenode

postgres=# select oid, relname, relfilenode,reltablespace
from pg_class
where relfilenode = 0 and relkind = 'r'
order by reltablespace;
 oid |    relname    | relfilenode | reltablespace
------+-----------------------+-------------+---------------
 1247 | pg_type        |      0 |       0
 1255 | pg_proc        |      0 |       0
 1249 | pg_attribute     |      0 |       0
 1259 | pg_class       |      0 |       0
 3592 | pg_shseclabel     |      0 |     1664
 1262 | pg_database      |      0 |     1664
 2964 | pg_db_role_setting  |      0 |     1664
 1213 | pg_tablespace     |      0 |     1664
 1261 | pg_auth_members    |      0 |     1664
 1214 | pg_shdepend      |      0 |     1664
 2396 | pg_shdescription   |      0 |     1664
 1260 | pg_authid       |      0 |     1664
 6000 | pg_replication_origin |      0 |     1664
 6100 | pg_subscription    |      0 |     1664
(14 rows)

postgres=#

上述查询可以看出,从pg_class系统表中查询出的这些表的relfilenode为0。其中pg_type、pg_proc、pg_attribute、pg_class是非共享表,在内核中称他们为Nail表。剩余的表是在pg_global表空间里的共享表。

pg_class表中relfilenode字段的意义是为了告诉程序,某一张表在磁盘上存储的文件名。比如我们查询t2表时,一定会先到pg_class系统表中获取其relfilenode,然后到磁盘找到这个文件,然后打开并扫描。可是如果我们想查询pg_class系统表在磁盘上的文件名时,应该去哪找到它的relfilenode?在PostgreSQL中提供了一组函数接口进行oid和relfilenode的转化。

postgres=# select pg_relation_filenode(1259);
 pg_relation_filenode
----------------------
        16475
(1 row)

postgres=# select pg_filenode_relation(0,16475);
 pg_filenode_relation
----------------------
 pg_class
(1 row)

postgres=# select pg_filenode_relation(0,16475)::oid;
 pg_filenode_relation
----------------------
         1259
(1 row)

postgres=#

通过pg_relation_filenode()可以将oid转化为relfilenode,
通过pg_filenode_relation可以将relfilenode转化为oid.
既然pg_class表中不存储oid和relfilenode的对应关系,那么PostgreSQL是怎么样保存这个映射关系的呢?

Nail表Relfilenode的存储机制

经过研究发现,在数据目录里存在着pg_filenode.map文件,如下所示。

movead@movead-PC:/h2/pgpgpg/data/base/12835$ ll pg_filenode.map
-rw-------+ 1 movead movead 512 12月 31 15:10 pg_filenode.map
movead@movead-PC:/h2/pgpgpg/data/base/12835$
movead@movead-PC:/h2/pgpgpg/data/global$ ll pg_filenode.map
-rw-------+ 1 movead movead 512 12月 31 15:10 pg_filenode.map
movead@movead-PC:/h2/pgpgpg/data/global$

在global目录下的pg_filenode.map文件里存储了shared表的oid和relfilenode的映射关系,12835目录下存储了OID为12835的数据库里nail表的oid和relfilenode的映射关系。
pg_filenode.map文件的结构为:

typedef struct RelMapping
{
  Oid     mapoid;     /* OID of a catalog */
  Oid     mapfilenode;  /* its filenode number */
} RelMapping;

typedef struct RelMapFile
{
  int32    magic;     /* always RELMAPPER_FILEMAGIC */
  int32    num_mappings;  /* number of valid RelMapping entries */
  RelMapping mappings[MAX_MAPPINGS];
  pg_crc32c  crc;      /* CRC of all above */
  int32    pad;      /* to make the struct size be 512 exactly */
} RelMapFile;

结语

这个博客主要阐述了在PostgreSQL中表的oid和relfilenode映射的两种不同表现形式,你只要记住使用pg_relation_filenode()永远会得到正确的结果,从pg_class系统表中查询则可能会得到错误的结果。

了解更多PostgreSQL技术干货、热点文集、行业动态、新闻资讯、精彩活动,请访问中国PostgreSQL社区网站:www.postgresqlchina.com

到此这篇关于PostgreSQL中Oid和Relfilenode的映射的文章就介绍到这了,更多相关PostgreSQL中Oid和Relfilenode的映射内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • postgresql 中的 like 查询优化方案

    当时数量量比较庞大的时候,做模糊查询效率很慢,为了优化查询效率,尝试如下方法做效率对比 一.对比情况说明: 1.数据量100w条数据 2.执行sql 二.对比结果 explain analyze SELECT c_patent, c_applyissno, d_applyissdate, d_applydate, c_patenttype_dimn, c_newlawstatus, c_abstract FROM public.t_knowl_patent_zlxx_temp WHERE c_a

  • 解决PostgreSQL Array使用中的一些小问题

    在PostgreSQL 中可以使用Array数据结构,例如 select array[1,2,3]; return {1,2,3} 但是,如果 select array[1,2,3][1]; --会报错 select (select array[1,2,3])[1] --可以使用 那么在用正则匹配函数 regexp_match 就会遇到这样的问题,如 select regexp_match('123-123', '(\d+)-(\d+)'); --return {123, 123} select

  • postgresql 实现replace into功能的代码

    PostgreSQL 9.5- 使用函数或with实现 create table test(id int primary key, info text, crt_time timestamp); with upsert as (update test set info='test',crt_time=now() where id=1 returning *) insert into test select 1,'test',now() where not exists (select 1 fro

  • PostgreSQL 禁用全表扫描的实现

    PostgreSQL可以通过一些设置来禁用全表扫描(FULL SCAN/Seq Scan) 注意: 设置此功能后不是完全避免全表扫描,而是只要有不通过全表扫描能得出结果的就不走全表扫描. 如果什么路都不通,那肯定得全表扫描,不然怎么获取数据. 而且并不是不走全表扫描性能就一定好. 下面展示下这个功能: 查询表结构: highgo=# \d test Table test Column | Type | Modifiers -------------+-----------------------

  • PostgreSQL regexp_matches替换like模糊查询的操作

    我就废话不多说了,大家还是直接看代码吧~ 改前: select * from 表名 where 字段名 like ||#{参数}||'%' 改后: select *,regexp_matches(字段名, #{参数}) from 表名 补充:postgresql实现模糊查询 正则表达式 因为数据库的查询操作比较单一,所以大部分的模糊查询操作都需要手动编写程序来实现. postgresql提供有强大的正则表达式系统,可以在数据库级别实现模糊查询. 正则表达式匹配操作符: 操作符 描述 例子 ~ 匹

  • 解析PostgreSQL中Oid和Relfilenode的映射问题

    作者李传成 中国PG分会认证专家,瀚高软件资深内核研发工程师 https://zhuanlan.zhihu.com/p/342466054 PostgreSQL中的表会有一个RelFileNode值指定这个表在磁盘上的文件名(外部表.分区表除外).一般情况下在pg_class表的relfilenode字段可以查出这个值,但是有一些特定表在relfilenode字段的查询结果是0,这个博客中将会探究这些特殊表relfilenode的内核处理. 正常表的Relfilenode 当我们创建一张普通表时

  • PostgreSQL中json数据类型详解

    目录 前言 一.PG数据库中JSON的类型 1.json和jsonb的区别 2.项目开发中的选择 3.json数据类型 二.PG中json的简单操作 1.基础json数据操作 2.json和jsonb输出对比 3.jsonb包含测试 总结 前言 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式.它基于 ECMAScript(European Computer Manufacturers Association, 欧洲计算机协会制定的js规范)的一个子集,

  • 在PostgreSQL中使用数组时值得注意的一些地方

    在Heap中,我们依靠PostgreSQL支撑大多数后端繁重的任务,我们存储每个事件为一个hstore blob,我们为每个跟踪的用户维护一个已完成事件的PostgreSQL数组,并将这些事件按时间排序. Hstore能够让我们以灵活的方式附加属性到事件中,而且事件数组赋予了我们强大的性能,特别是对于漏斗查询,在这些查询中我们计算不同转化渠道步骤间的输出. 在这篇文章中,我们看看那些意外接受大量输入的PostgreSQL函数,然后以高效,惯用的方式重写它. 你的第一反应可能是将PostgreSQ

  • 通过实例解析java8中的parallelStream

    这篇文章主要介绍了通过实例解析java8中的parallelStream,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 about Stream 什么是流? Stream是java8中新增加的一个特性,被java猿统称为流. Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator.原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作:高级版本的 Stream

  • 浅谈PostgreSQL中的孤儿文件用法(orphaned data files)

    创建一个测试表 postgres=# create table t1(a int); CREATE TABLE postgres=# select pg_relation_filepath('t1'); pg_relation_filepath ---------------------- base/75062/75297 (1 row) postgres=# 在操作系统上已经可以看到该文件. $ ls -la $PGDATA/base/75062/75297 -rw------- 1 post

  • PostgreSQL中的VACUUM命令用法说明

    每当PostgreSQL数据库中的表中的行被更新或删除时,死亡行会被遗留下来.VACUUM则会把它们除去来使空间能被重新利用.如果一个表没有被清空,它会变得臃肿,浪费磁盘空间而且会降低顺序表扫描的速度,而且在较小范围内也会降低索引扫描的速度. VACUUM命令只可以移除这些不再被需要的行版本(也被称为元组).如果被删除事务的事务ID(存储在xmax系统列中)比仍然活跃在PostgreSQL数据库(或者共享表的整个集群)中最老的事务(xmin界限)更老,那么这个元组将不再被需要. 注意以下三种情况

  • 解析 MyBatis 中 Mapper 生效的来龙去脉

    目录 一.MyBatis基本使用 1.1 编写配置文件 1.2 编写Mapper接口及测试方法 1.3 结果 二.源码分析 2.1 通过配置文件构建出 SqlSessionFactory 2.2 获取 SqlSession 对象 2.3 根据 SqlSession 获取 Mapper 代理 2.4 通过 Mapper 代理,执行方法操作数据库 2.5 整体流程图 最近闲了快有半个多月了,一直提不起兴致再去看一些书籍(没有以前疯狂吸食知识的欲望了

  • 深度解析SpringBoot中@Async引起的循环依赖

    目录 事故时间线 猜想 什么是循环依赖 什么是@Async 啊,昨晚发版又出现了让有头大的循环依赖问题,按理说Spring会为我们解决循环依赖,但是为什么还会出现这个问题呢?为什么在本地.UAT以及PRE环境都没有出现这个问题,但是到了PROD环境就出现了这个问题呢?本文将从事故时间线.及时止损.复盘分析等几个方面为大家带来详细的分析,干货满满! 事故时间线 本着"先止损.后复盘分析"的原则,我们来看一下这次发版事故的时间线. 2021年11月16日晚23点00分00秒开始发版,此时集

  • 源码解析python中randint函数的效率缺陷

    目录 一.前言 二.对randint()运行效率的测试 三.从源码分析randint()的缺陷 random.random() random.randint() 四.更快的生成随机整数的方法 random.random() 直接使用 getrandbits() 使用 Numpy.random 一.前言 前几天,在写一个与差分隐私相关的简单程序时,我发现了一些奇怪的东西:相对于其他的随机数生成函数,Python的random.randint()函数感觉很慢. 由于 randint() 是 Pyth

  • 利用Mybatis向PostgreSQL中插入并查询JSON字段

    目录 应用场景介绍 数据insert 数据select BATCH 批量插入 前言: 这里我使用的是TimescaleDB,加了一个时间戳字段,不过没差.关于PostgreSQL中Json数据类型的操作,可以参考官网. 应用场景介绍 将TCP发过来的数据包(通过消息队列发过来)解析出数据(一个数据包含有多帧,一帧中含有多条信息),并和本地规则表的格式对应起来.以JsonLineMsg实体类代表对应的一帧数据: package tsdb.entity; import lombok.AllArgsC

随机推荐