今天在网上看到有人问误删pg_filenode.map该如何恢复或者重建,解决这个问题前我们先来了解下pg_filenode.map文件。
对于PostgreSQL中的每张表在磁盘上都有与之相关的文件,而这些文件的名字便是relfilenode,我们可以通过pg_class的relfilenode字段去查询。
但是有一部分特殊的表我们会发现其对应的该字段为0,官方文档的解释为:0表示这是一个“映射”关系,其磁盘文件名取决于低层状态。那么哪些表的relfilenode字段会是0呢?这些relfilenode为0的表对应的文件又该如何去查找呢?
我们都知道对于一张普通表,其relfilenode和oid默认是一样的,例如:
bill=# create table t(id int); CREATE TABLE bill=# select oid,relname,relfilenode from pg_class where relname = 't'; oid | relname | relfilenode -------+---------+------------- 24919 | t | 24919 (1 row)
但是当我们对该表进行了例如vacuum full、truncate之类的操作后,那么relfilenode便会发生变化:
bill=# vacuum FULL t; VACUUM bill=# select oid,relname,relfilenode from pg_class where relname = 't'; oid | relname | relfilenode -------+---------+------------- 24919 | t | 24922 (1 row)
那么我们再来看看那些relfilenode为0的表:
bill=# select oid, relname, relfilenode,reltablespace bill-# from pg_class bill-# where relfilenode = 0 and relkind = 'r' bill-# order by reltablespace; oid | relname | relfilenode | reltablespace ------+-----------------------+-------------+--------------- 1247 | pg_type | 0 | 0 1249 | pg_attribute | 0 | 0 1255 | pg_proc | 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 6100 | pg_subscription | 0 | 1664 6000 | pg_replication_origin | 0 | 1664 (14 rows)
这些表通过reltablespace字段我们也可以发现分为两类:一类是pg_type、pg_attribute、pg_proc和pg_class,它们是非共享的表,在内核中我们称为Nail表。而另一类则是reltablespace为1664的,即在pg_global表空间里的共享表。
而为什么这些系统表的relfilenode为0呢?因为对于这种访问十分频繁的系统表,我们不希望每次都是从一些其它的系统表去查询,这样性能便会非常低,它们便是通过pg_filenode.map文件去进行管理的。
在pg_filenode.map文件中,将这些系统表的oid与relfileno做映射,而这个文件的大小为512,刚好是一个OS disk sector的大小。同时PG做了对齐处理,在源码上用RelMapFile结构体与之对应。结构体大小为:628+44=496+16=512。也就是说这个文件最多存放62条系统catalog表的记录。
由于这个文件的重要性,刚好与disk sector大小对齐,减少文件crash的机率。
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;
接着我们来具体看看这个文件里面存放的内容是什么样的:
如上图所示,可以将该文件分为四个部分。
第一部分:2717 0059,表示文件头魔法数据字。
#define RELMAPPER_FILEMAGIC0x592717/* version ID value */
第二部分:0011 0000,表示文件中包含的映射对象数。我们可以通过以下SQL验证:
刚好是17行数据,和前面0011一致。
bill=# select relname,relfilenode from pg_class where pg_relation_filepath(oid) like 'base/16385/%' and relfilenode = 0; relname | relfilenode -----------------------------------+------------- pg_toast_1255 | 0 pg_toast_1255_index | 0 pg_proc_oid_index | 0 pg_proc_proname_args_nsp_index | 0 pg_type_oid_index | 0 pg_type_typname_nsp_index | 0 pg_attribute_relid_attnam_index | 0 pg_attribute_relid_attnum_index | 0 pg_class_oid_index | 0 pg_class_relname_nsp_index | 0 pg_class_tblspc_relfilenode_index | 0 pg_attribute | 0 pg_proc | 0 pg_type | 0 pg_toast_1247 | 0 pg_toast_1247_index | 0 pg_class | 0 (17 rows)
第三部分:04eb 0000 4095 0000,这一类便是实际的映射关系04eb即对象的oid,4095表示对象的relfilenode。
第四部分:ebfa f3a4,文件尾的校验值,通过crc32算法对文件名计算得出:
/* verify the CRC */
INIT_CRC32C(crc);
COMP_CRC32C(crc, (char *) map, offsetof(RelMapFile, crc));
FIN_CRC32C(crc);
if (!EQ_CRC32C(crc, map->crc))
ereport(FATAL,
(errmsg(“relation mapping file “%s” contains incorrect checksum”,
mapfilename)));
介绍完pg_filenode.map文件我们来回答前面的问题,如果误删了该文件怎么办呢?
如果该文件某个数据库下的该文件删除,那么该库便无法连接:
pg14@vm-192-168-204-153-> psql bill bill
psql: error: connection to server on socket “/home/pg14/pgdata/.s.PGSQL.2022” failed: FATAL: could not open file “base/16385/pg_filenode.map”: No such file or directory
但是不会影响其它库的连接:
pg14@vm-192-168-204-153-> psql postgres
psql (14.1)
Type “help” for help.
postgres=#
而如果global目录下的该文件误删,那么所有库均无法连接:
pg14@vm-192-168-204-153-> psql postgres
psql: error: connection to server on socket “/home/pg14/pgdata/.s.PGSQL.2022” failed: FATAL: could not open file “global/pg_filenode.map”: No such file or directory
pg14@vm-192-168-204-153-> psql bill
psql: error: connection to server on socket “/home/pg14/pgdata/.s.PGSQL.2022” failed: FATAL: could not open file “global/pg_filenode.map”: No such file or directory
因此我们可以知道,当pg_filenode.map文件损坏或者被误删后,那么至少对应的库肯定是连接不上了,那么我们也没办法手工去创建该文件了。
不过一般来说大部分库的这个文件都是一样的,很少会去对该文件进行修改,除非你对这些系统表进行了vacuum full之类的操作。而如果你真的不幸该库做过类似操作那该怎么办呢?只好先从其它库拷贝一个文件过来,然后pg_filedump本库的pg_class的数据文件看看相应的系统表的信息,然后要注意,不要去直接修改pg_filenode.map文件,而是要去修改数据文件的文件名,让其满足pg_filenode.map中的映射关系。
为什么呢?我们前面说过该文件尾部有一个根据文件名进行计算的校验值,如果直接修改pg_filenode.map文件的话,则会提示校验值不对:
psql: error: connection to server on socket “/home/pg14/pgdata/.s.PGSQL.2022” failed: FATAL: relation mapping file “base/16385/pg_filenode.map” contains incorrect checksum
总结:
误删pg_filenode.map怎么办?首先运气好的话从别的库拷贝一个该文件,如果可用的话就可以直接使用。
而如果你的库上该文件中的系统表做过vacuum full之类的操作,那么便需要通过类似pg_filedump的方式去pg_class的数据文件中获取相关的信息,然后手动修改相关数据文件名让其满足pg_filenode.map中的映射关系。
当然该方法会很麻烦,所以还是要注意千万别误删了你的pg_filenode.map!
到此这篇关于PostgreSQL pg_filenode.map文件介绍的文章就介绍到这了,更多相关PostgreSQL pg_filenode.map内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!