PostgreSQL控制文件恢复

关于控制文件方面

Oracle:

1.控制文件是oracle数据库最重要的物理文件之一。每个oracle 数据库都必须至少有一个控制文件。在启动数据库实例时,oracle会根据初始化参数找到控制文件,并读取控制文件中的内容。然后根据控制文件中的信息(如数据库名称、数据文件和日志文件的名称和位置等)在实例和数据库之间建立起连接。
2.控制文件是一个二进制文件,它记录了数据库的物理结构,其中主要包括数据库名、数据文件、日志文件的名字和位置。
当oracle实例在正常启动时,系统首先要访问的是初始化参数文件spfile,然后oracle为系统全局区(SGA)分配内存,。这时oralce实例处于安装状态,并且控制文件处于打开状态,接下来oracle会自动读取出控制文件的所有数据文件和日志文件信息,并打开所有数据库中的所有数据文件和所有的日志文件以供用户访问。
oracle提供了备份文件和多路复用的机制。oracle数据库的控制文件是在创建数据库时自动创建的,一般情况下,至少需要一个副本。
 
同理PostgreSQL控制文件原理一样,只不过PG控制文件不像Oracle那样冗余、安全。
 
如果无法找到控制文件或控制文件损坏,则数据库实例将无法启动,并且很难修复。
 
(1)PG控制文件位置:

[postgres@pgccc01 bin]$ ls -la $PGDATA/global/pg_control
-rw-------. 1 postgres postgres 8192 Jun 9 10:50 /data/pg_data/global/pg_control

存储在pg_global表空间中,我们知道PG默认有两个表空间,一个是 pg_global,另一个是pg_default表空间,我们会在initdb初始化时形成。

 

postgres=# select * from pg_tablespace ;
oid | spcname | spcowner | spcacl | spcoptions
-------+--------------------+----------+-------------------+------------
1663 | pg_default | 10 | |
1664 | pg_global | 10 | |
16456 | tablespace_andyxi2 | 10 | |                   ##后面创建的表空间
16431 | tablespace_andyxi | 16389 | {andyxi=C/andyxi} |     ##后面创建的表空间
(4 rows)

(2)查看pg_controldata文件的内容

  有两种方式,

一种是通过SQL查询,如下:

select * from pg_catalog.pg_control_checkpoint();

select * from pg_catalog.pg_control_system();
select * from pg_catalog.pg_control_init(); -- 获取结果和pg_controldata相同
select * from pg_catalog.pg_control_recovery();

另一种是:

 

[postgres@pgccc01 bin]$ pg_controldata
pg_control version number: 1300
Catalog version number: 202209061
Database system identifier: 7238474806429805345
Database cluster state: in production
pg_control last modified: Fri 09 Jun 2023 10:50:42 AM CST
Latest checkpoint location: 0/10442160
Latest checkpoint's REDO location: 0/10442128
Latest checkpoint's REDO WAL file: 000000010000000000000010
Latest checkpoint's TimeLineID: 1
Latest checkpoint's PrevTimeLineID: 1
Latest checkpoint's full_page_writes: on
Latest checkpoint's NextXID: 0:810
Latest checkpoint's NextOID: 16471
Latest checkpoint's NextMultiXactId: 1
Latest checkpoint's NextMultiOffset: 0
Latest checkpoint's oldestXID: 717
Latest checkpoint's oldestXID's DB: 5
Latest checkpoint's oldestActiveXID: 810
Latest checkpoint's oldestMultiXid: 1
Latest checkpoint's oldestMulti's DB: 16392
Latest checkpoint's oldestCommitTsXid:0
Latest checkpoint's newestCommitTsXid:0
Time of latest checkpoint: Fri 09 Jun 2023 10:49:11 AM CST
Fake LSN counter for unlogged rels: 0/3E8
Minimum recovery ending location: 0/0
Min recovery ending loc's timeline: 0
Backup start location: 0/0
Backup end location: 0/0
End-of-backup record required: no
wal_level setting: replica
wal_log_hints setting: off
max_connections setting: 100
max_worker_processes setting: 8
max_wal_senders setting: 10
max_prepared_xacts setting: 0
max_locks_per_xact setting: 64
track_commit_timestamp setting: off
Maximum data alignment: 8
Database block size: 8192
Blocks per segment of large relation: 131072
WAL block size: 8192
Bytes per WAL segment: 16777216
Maximum length of identifiers: 64
Maximum columns in an index: 32
Maximum size of a TOAST chunk: 1996
Size of a large-object chunk: 2048
Date/time type storage: 64-bit integers
Float8 argument passing: by value
Data page checksum version: 0
Mock authentication nonce: 86f315bd65a4f0a2eb33d3001509754570cfcef6f0858c1bf2759b1612615131

(3)我们现在模拟pg_control这个控制文件损坏,进一步分析和恢复

我们在做实验之前,来了解一下重建控制命令(PostgreSQL10版本以后):

[postgres@pgccc01 pg_wal]$ pg_resetwal --help
pg_resetwal resets the PostgreSQL write-ahead log.

Usage:
pg_resetwal [OPTION]... DATADIR

Options:
-c, --commit-timestamp-ids=XID,XID
set oldest and newest transactions bearing
commit timestamp (zero means no change)
[-D, --pgdata=]DATADIR data directory
-e, --epoch=XIDEPOCH set next transaction ID epoch
-f, --force force update to be done
-l, --next-wal-file=WALFILE set minimum starting location for new WAL
-m, --multixact-ids=MXID,MXID set next and oldest multitransaction ID
-n, --dry-run no update, just show what would be done
-o, --next-oid=OID set next OID
-O, --multixact-offset=OFFSET set next multitransaction offset
-u, --oldest-transaction-id=XID set oldest transaction ID
-V, --version output version information, then exit
-x, --next-transaction-id=XID set next transaction ID
--wal-segsize=SIZE size of WAL segments, in megabytes
-?, --help show this help, then exit

Report bugs to <pgsql-bugs@lists.postgresql.org>.
PostgreSQL home page: <https://www.postgresql.org/>

同时,我们先了解下数据库目录文件有哪些:

[postgres@pgccc01 pg_data]$ ls -la
total 68
drwx------. 20 postgres postgres 4096 Jun 9 10:14 .
drwxr-xr-x. 9 postgres postgres 269 Jun 7 15:36 ..
drwx------. 7 postgres postgres 63 May 30 13:39 base
drwx------. 2 postgres postgres 4096 Jun 9 10:14 global
drwx------. 2 postgres postgres 6 May 29 13:39 pg_commit_ts
drwx------. 2 postgres postgres 6 May 29 13:39 pg_dynshmem
-rw-------. 1 postgres postgres 4789 May 29 13:39 pg_hba.conf
-rw-------. 1 postgres postgres 1636 Jun 8 15:39 pg_ident.conf
drwx------. 4 postgres postgres 68 Jun 9 10:49 pg_logical
drwx------. 4 postgres postgres 36 May 29 13:39 pg_multixact
drwx------. 2 postgres postgres 6 May 29 13:39 pg_notify
drwx------. 2 postgres postgres 6 May 29 13:39 pg_replslot
drwx------. 2 postgres postgres 6 May 29 13:39 pg_serial
drwx------. 2 postgres postgres 6 May 29 13:39 pg_snapshots
drwx------. 2 postgres postgres 6 Jun 9 10:14 pg_stat
drwx------. 2 postgres postgres 6 May 29 13:39 pg_stat_tmp
drwx------. 2 postgres postgres 18 May 29 13:39 pg_subtrans
drwx------. 2 postgres postgres 32 Jun 7 15:37 pg_tblspc
drwx------. 2 postgres postgres 6 May 29 13:39 pg_twophase
-rw-------. 1 postgres postgres 3 May 29 13:39 PG_VERSION
drwx------. 3 postgres postgres 92 Jun 9 10:50 pg_wal
drwx------. 2 postgres postgres 18 May 29 13:39 pg_xact
-rw-------. 1 postgres postgres 88 Jun 7 09:25 postgresql.auto.conf
-rw-------. 1 postgres postgres 29804 Jun 2 13:26 postgresql.conf
-rw-------. 1 postgres postgres 27 Jun 9 10:14 postmaster.opts
-rw-------. 1 postgres postgres 71 Jun 9 10:14 postmaster.pid
drwxrwxr-x. 3 postgres postgres 29 May 30 13:51 tablespace_andyxi

我们恢复控制文件,需要先了解一下这几个数据库目录文件

[postgres@pgccc01 pg_data]$ ls -la pg_xact/
total 12
drwx------. 2 postgres postgres 18 May 29 13:39 .
drwx------. 20 postgres postgres 4096 Jun 9 10:14 ..
-rw-------. 1 postgres postgres 8192 Jun 9 10:49 0000
[postgres@pgccc01 pg_data]$ ls -la pg_wal/
total 32772
drwx------. 3 postgres postgres 92 Jun 9 10:50 .
drwx------. 20 postgres postgres 4096 Jun 9 10:14 ..
-rw-------. 1 postgres postgres 16777216 Jun 9 10:50 000000010000000000000010
-rw-------. 1 postgres postgres 16777216 Jun 9 10:48 000000010000000000000011
drwx------. 2 postgres postgres 6 Jun 2 13:31 archive_status
[postgres@pgccc01 pg_data]$ ls -la pg_multixact/
total 4
drwx------. 4 postgres postgres 36 May 29 13:39 .
drwx------. 20 postgres postgres 4096 Jun 9 10:14 ..
drwx------. 2 postgres postgres 18 May 29 13:39 members
drwx------. 2 postgres postgres 18 May 29 13:39 offsets
[postgres@pgccc01 pg_data]$ ls -la pg_multixact/members/
total 8
drwx------. 2 postgres postgres 18 May 29 13:39 .
drwx------. 4 postgres postgres 36 May 29 13:39 ..
-rw-------. 1 postgres postgres 8192 May 29 13:39 0000
[postgres@pgccc01 pg_data]$ ls -la pg_multixact/offsets/
total 8
drwx------. 2 postgres postgres 18 May 29 13:39 .
drwx------. 4 postgres postgres 36 May 29 13:39 ..
-rw-------. 1 postgres postgres 8192 Jun 9 10:19 0000

 控制文件的恢复涉及到16进制的转换问题,如:

 

模拟pg_control不存在

 PostgreSQL不能启动

 

 

pg_resetwal最关键的步骤:

1.参数-l

-l, --next-wal-file=WALFILE set minimum starting location for new WAL

通过指定下一个WAL段文件名称来手工设置WAL开始位置

下一个WAL段文件的名称应该比当前存在于数据目录下pg_wal目录中的任意 WAL 段文件名更大。这些名称也是十六进制的并且有三个部分。第一部分是时间线 ID”并且通常应该被保持相同。例如,如果00000001000000320000004Apg_wal中最大的项,则使用-l 00000001000000320000004B或更高的值。

进入pg_wal(相当于redo log)

[postgres@pgccc01 pg_data]$ cd /data/pg_data/pg_wal/
[postgres@pgccc01 pg_wal]$ ls -la
total 32772
drwx------. 3 postgres postgres 92 Jun 9 10:50 .
drwx------. 20 postgres postgres 4096 Jun 9 13:31 ..
-rw-------. 1 postgres postgres 16777216 Jun 9 13:29 000000010000000000000010
-rw-------. 1 postgres postgres 16777216 Jun 9 10:48 000000010000000000000011
drwx------. 2 postgres postgres 6 Jun 2 13:31 archive_status

此时最新文件是000000010000000000000011 需要编号+1,000000010000000000000012(16进制)

2.参数-O

-O, --multixact-offset=OFFSET set next multitransaction offset

 

手工设置下一个多事务偏移量。

 

确定安全值的方法:查找数据目录下pg_multixact/members目录中最大的数字文件名,然后在它的基础上加一并且乘以 52352 (0xCC80)。文件名是十六进制数字。没有像其他选项那样追加零的简单方法

 

上述pg_multixact日志0000文件最大值为0,一般这个数不变

取最大值加1,然后乘以65536,再转换成16进制为10000,然后末尾添加5个0

 -O=0x1000000000

3.参数-m

-m, --multixact-ids=MXID,MXID set next and oldest multitransaction ID

 

手工设置下一个和最老的多事务 ID。

 

确定下一个多事务 ID(第一部分)的安全值的方法:在数据目录下的pg_multixact/offsets目录中查找最大的数字文件名,然后在它的基础上加一并且乘以 65536 (0x10000)。反过来,确定最老的多事务 ID(-m的第二部分)的方法:在同一个目录中查找最小的数字文件名并且乘以 65536。文件名是十六进制的数字,因此实现上述方法最简单的方式是以十六进制指定选项值并且追加四个零。

 

日志0000文件最大值为0,一般这个数不变,取最大值编号后加1,然后末尾添加4个0

-m=0x00010000,0x00010000

 

4 参数-x

-x, --next-transaction-id=XID    set next transaction ID

手工设置下一个事务 ID。

确定安全值的方法:在数据目录下的pg_xact目录中查找最大的数字文件名,然后在它的基础上加一并且乘以 1048576 (0x100000)。注意文件名是十六进制的数字。通常以十六进制的形式指定该选项值也是最容易的。例如,如果0011pg_xact中的最大项,-x 0x1200000就可以(五个尾部的零就表示了前面说的乘数)。

 j最大文件编码取最大值加1,然后末尾添加5个0

-x=0x000100000

 

在global表空间创建pg_control空文件

 

最后用pg_resetwal命令加上相应参数进行恢复

pg_resetwal -l 000000010000000000000012  -O 0x1000000000  -m 0x00010000,0x00010000 -x 0x000100000 -f $PGDATA

这个命令不能在服务器正在运行时被使用。如果在数据目录中发现一个服务器锁文件,pg_resetwal将拒绝启动。如果服务器崩溃那么一个锁文件可能会被留下,在那种情况下你能移除该锁文件来让pg_resetwal运行。但是在你那样做之前,再次确认没有服务器进程仍然存活。

 删除postmaster.pid后,显示

 这时pg_control回来了

 pg_controldata也可以运行了

[postgres@pgccc01 pg_data]$ pg_controldata

PostgreSQL启动正常:

 

 

总结:

pg_resetwal会清除预写式日志(WAL)并且有选择地重置存储在pg_control文件中的一些其他控制信息。如果这些文件已经被损坏,某些时候就需要这个功能。当服务器由于这样的损坏而无法启动时,这只应该被用作最后的手段。

在运行这个命令之后,就可能可以启动服务器,但是记住数据库可能包含由于部分提交事务产生的不一致数据。你应当立刻转储你的数据、运行initdb并且重新载入。重新载入后,检查不一致并且根据需要修复之。

这个工具只能被安装服务器的用户运行,因为它要求对数据目录的读写访问。出于安全原因,你必须在命令行中指定数据目录。pg_resetwal不使用环境变量PGDATA

如果pg_resetwal抱怨它无法为pg_control决定合法数据,你可以通过指定-f(强制)选项强制它继续。在这种情况下,丢失的数据将被替换为看似合理的值。可以期望大部分域是匹配的,但是下一个 OID、下一个事务 ID 和纪元、下一个多事务 ID 和偏移以及 WAL 开始位置域可能还是需要人工协助。这些域可以使用下面讨论的选项设置。如果你不能为所有这些域决定正确的值,-f还是可以被使用,但是恢复的数据库还是值得怀疑:一次立即的转储和重新载入是势在必行的。在你转储之前不要在该数据库中执行任何数据修改操作,因为任何这样的动作都可能使破坏更严重。

 

posted @ 2023-06-09 14:27  青空如璃  阅读(351)  评论(0编辑  收藏  举报