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: 1300Catalog version number: 202209061Database system identifier: 7238474806429805345Database cluster state: in productionpg_control last modified: Fri 09 Jun 2023 10:50:42 AM CSTLatest checkpoint location: 0/10442160Latest checkpoint"s REDO location: 0/10442128Latest checkpoint"s REDO WAL file: 000000010000000000000010Latest checkpoint"s TimeLineID: 1Latest checkpoint"s PrevTimeLineID: 1Latest checkpoint"s full_page_writes: onLatest checkpoint"s NextXID: 0:810Latest checkpoint"s NextOID: 16471Latest checkpoint"s NextMultiXactId: 1Latest checkpoint"s NextMultiOffset: 0Latest checkpoint"s oldestXID: 717Latest checkpoint"s oldestXID"s DB: 5Latest checkpoint"s oldestActiveXID: 810Latest checkpoint"s oldestMultiXid: 1Latest checkpoint"s oldestMulti"s DB: 16392Latest checkpoint"s oldestCommitTsXid:0Latest checkpoint"s newestCommitTsXid:0Time of latest checkpoint: Fri 09 Jun 2023 10:49:11 AM CSTFake LSN counter for unlogged rels: 0/3E8Minimum recovery ending location: 0/0Min recovery ending loc"s timeline: 0Backup start location: 0/0Backup end location: 0/0End-of-backup record required: nowal_level setting: replicawal_log_hints setting: offmax_connections setting: 100max_worker_processes setting: 8max_wal_senders setting: 10max_prepared_xacts setting: 0max_locks_per_xact setting: 64track_commit_timestamp setting: offMaximum data alignment: 8Database block size: 8192Blocks per segment of large relation: 131072WAL block size: 8192Bytes per WAL segment: 16777216Maximum length of identifiers: 64Maximum columns in an index: 32Maximum size of a TOAST chunk: 1996Size of a large-object chunk: 2048Date/time type storage: 64-bit integersFloat8 argument passing: by valueData page checksum version: 0Mock authentication nonce: 86f315bd65a4f0a2eb33d3001509754570cfcef6f0858c1bf2759b1612615131
(3)我们现在模拟pg_control这个控制文件损坏,进一步分析和恢复
我们在做实验之前,来了解一下重建控制命令(PostgreSQL10版本以后):
[postgres@pgccc01 pg_wal]$ pg_resetwal --helppg_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
同时,我们先了解下数据库目录文件有哪些:
[postgres@pgccc01 pg_data]$ ls -latotal 68drwx------. 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 basedrwx------. 2 postgres postgres 4096 Jun 9 10:14 globaldrwx------. 2 postgres postgres 6 May 29 13:39 pg_commit_tsdrwx------. 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.confdrwx------. 4 postgres postgres 68 Jun 9 10:49 pg_logicaldrwx------. 4 postgres postgres 36 May 29 13:39 pg_multixactdrwx------. 2 postgres postgres 6 May 29 13:39 pg_notifydrwx------. 2 postgres postgres 6 May 29 13:39 pg_replslotdrwx------. 2 postgres postgres 6 May 29 13:39 pg_serialdrwx------. 2 postgres postgres 6 May 29 13:39 pg_snapshotsdrwx------. 2 postgres postgres 6 Jun 9 10:14 pg_statdrwx------. 2 postgres postgres 6 May 29 13:39 pg_stat_tmpdrwx------. 2 postgres postgres 18 May 29 13:39 pg_subtransdrwx------. 2 postgres postgres 32 Jun 7 15:37 pg_tblspcdrwx------. 2 postgres postgres 6 May 29 13:39 pg_twophase-rw-------. 1 postgres postgres 3 May 29 13:39 PG_VERSIONdrwx------. 3 postgres postgres 92 Jun 9 10:50 pg_waldrwx------. 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.piddrwxrwxr-x. 3 postgres postgres 29 May 30 13:51 tablespace_andyxi
我们恢复控制文件,需要先了解一下这几个数据库目录文件
[postgres@pgccc01 pg_data]$ ls -la pg_xact/total 12drwx------. 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 32772drwx------. 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 000000010000000000000011drwx------. 2 postgres postgres 6 Jun 2 13:31 archive_status[postgres@pgccc01 pg_data]$ ls -la pg_multixact/total 4drwx------. 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 membersdrwx------. 2 postgres postgres 18 May 29 13:39 offsets[postgres@pgccc01 pg_data]$ ls -la pg_multixact/members/total 8drwx------. 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 8drwx------. 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”并且通常应该被保持相同。例如,如果00000001000000320000004A
是pg_wal
中最大的项,则使用-l 00000001000000320000004B
或更高的值。
进入pg_wal(相当于redo log)
[postgres@pgccc01 pg_data]$ cd /data/pg_data/pg_wal/[postgres@pgccc01 pg_wal]$ ls -latotal 32772drwx------. 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 000000010000000000000011drwx------. 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)。注意文件名是十六进制的数字。通常以十六进制的形式指定该选项值也是最容易的。例如,如果0011
是pg_xact
中的最大项,-x 0x1200000
就可以(五个尾部的零就表示了前面说的乘数)。
j最大文件编码取最大值加1,然后末尾添加5个0
-x=0x000100000
在global表空间创建pg_control空文件
最后用pg_resetwal命令加上相应参数进行恢复
pg_resetwal -l000000010000000000000012 -O0x1000000000 -m0x00010000,0x00010000 -x0x000100000 -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
还是可以被使用,但是恢复的数据库还是值得怀疑:一次立即的转储和重新载入是势在必行的。在你转储之前不要在该数据库中执行任何数据修改操作,因为任何这样的动作都可能使破坏更严重。
标签: