文章来源: http://blog.sina.com.cn/s/blog_74a7d3390102yksv.html
PostgreSQL控制文件内容:
主要分为是三部分,初始化静态信息、WAL及检查点的动态信息、一些配置信息。
我们可以用过pg_controldata命令直接读取PostgreSQL控制文件内容:
[postgres@postgresdb ~]$ /u01/postgres/pgsql/bin/pg_controldata -D /data/postgres/data pg_control version number: 1100 Catalog version number: 201809051 Database system identifier: 6709564017377676696 Database cluster state: in production pg_control last modified: Wed 17 Jul 2019 02:27:12 PM HKT Latest checkpoint location: 5A/F522A8E0 Latest checkpoint's REDO location: 5A/F522A8A8 Latest checkpoint's REDO WAL file: 000000010000005A000000F5 Latest checkpoint's TimeLineID: 1 Latest checkpoint's PrevTimeLineID: 1 Latest checkpoint's full_page_writes: on Latest checkpoint's NextXID: 0:57914 Latest checkpoint's NextOID: 73874 Latest checkpoint's NextMultiXactId: 1 Latest checkpoint's NextMultiOffset: 0 Latest checkpoint's oldestXID: 561 Latest checkpoint's oldestXID's DB: 1 Latest checkpoint's oldestActiveXID: 57914 Latest checkpoint's oldestMultiXid: 1 Latest checkpoint's oldestMulti's DB: 1 Latest checkpoint's oldestCommitTsXid:0 Latest checkpoint's newestCommitTsXid:0 Time of latest checkpoint: Wed 17 Jul 2019 02:27:07 PM HKT Fake LSN counter for unlogged rels: 0/1 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_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: 1310720 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 Float4 argument passing: by value Float8 argument passing: by value Data page checksum version: 0 Mock authentication nonce: ce6a83651b3b6ba8a8c8fcc8ee22ffb9c47d1aebaf3dae82462d23826c10f26f [postgres@postgresdb ~]$
下面详细介绍下各参数含义。
pg_control version number是控制文件版本号。
Catalog version number 是系统表版本号,格式是yyyymmddN。记录系统不兼容性的改变。N是yyymmdd当天改变的次数。具体可以查看源码文件catversion.h。
Database system identifier 数据库系统号 这个标识串是一个64bit的整数,其中包含了创建数据库的时间戳和initdb时初始化的进程号,具体初始化方法可查看源码文件xlog.c。
创建时间可以通过to_timestamp转换查看到。
postgres=# SELECT to_timestamp(((6709564017377676696>>32) & (2^32 -1)::bigint)); to_timestamp ------------------------ 2019-07-04 06:15:08+08
Database cluster state 记录实例的状态。源码文件中看到数据库的几种状态,源码pg_control.h中可以看到:
starting up:表示数据库正在启动状态。 shut down: 数据库实例(非Standby)正常关闭后控制文件中就是此状态。 shut down in recovery:Standby实例正常关闭后控制文件中就是此状态。 shutting down:正常停库时,先做checkpoint,开始做checkpoint时,会把状态设置为此状态,做完后把状态设置为shut down。 in crash recovery:数据库实例非异常停止后,重新启动后,会先进行实例的恢复,在实例恢复时的状态就是此状态。 in archive recovery:Standby实例正常启动后,就是此状态。 in production:数据库实例正常启动后就是此状态。Standby数据库正常启动后不是此状态 Latest checkpoint location数据库异常停止后再重新启动时,需要做实例恢复,实例恢复的过程是从WAL日志中,找到最后一次的checkpoint点,然后读取这个点之后的WAL日志,重新应用这些日志,此过程称为数据库实例前滚,最后一次的checkpoint点的信息记录在Latest checkpont项中。 Latest checkpoint's REDO location 记录数据库日志文件上检查点。 Latest checkpoint's REDO WAL file记录WAL日志名,目录下pg_wal可以查到文件。 Latest checkpoint's NextXID前面是新纪元值,冒号后面是下一个事务号,当前事务号最大值安全值可以在pg_xact目录下通过文件名计算出来。 Latest checkpoint's NextMultiXactId参数,可以通过pg_multixact/offsets文件名计算出来安全值。 Latest checkpoint's NextMultiOffset参数,当恢复控制文件时可以通过pg_multixact/members文件夹下计算出此参数的安全值。 Maximum length of identifiers是指一些数据库对象名称的最大长度,如表名、索引名的最大长度 Maximum columns in an index 表示一个索引最多多少列,目前为32个。 Maximum size of a TOAST chunk是TOAST chunk的最大长度。TOAST是解决当列的内容太长,在一个数据块中存不下时的一种行外存储的方式。类似Oracle的行链接。 Data page checksum version是数据块checksum的版本,默认为0,数据块没有使用checksum。运行initdb时加了-k参数,PG才会在数据块上启用checksum功能。 参数介绍到这里,控制文件各内容定义可以查看源文件pg_control.h。 PostgreSQL控制文件重建 pg9.6前使用 pg_resetxlog,pg10之后使用pg_resetwal清理wal日志或重置控制文件中一些控制信息。 命令详细介绍可以查看官方文档: https://www.postgresql.org/docs/11/app-pgresetwal.html [postgres@lsl-test1 ~]$ /u01/postgres/pgsql/bin/pg_resetwal -n -D /data/postgres/data pg_resetwal: lock file "postmaster.pid" exists Is a server running? If not, delete the lock file and try again. [postgres@lsl-test1 ~]$ /u01/postgres/pgsql/bin/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 -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
下面看下命令各个参数具体含义:
-c参数有两个参数值,一个最旧的事务号,一个最新的事务号。最旧的事务号的安全值,可以在pg_commit_ts目录查询最小的文件名;
最新事务ID的安全值,可以在pg_commit_ts目录查询最大的文件名。文件名都是16进制。实际测试在11的版本pg_commit_ts目录下未发现文件。
[postgres@lsl-test1 data]$ cd /data/postgres/data/pg_commit_ts/ [postgres@lsl-test1 pg_commit_ts]$ ls -l
-e参数是设置事务号的新纪元(epoch),除了pg_resetwal设置的字段之外,事务ID新纪元实际上并不存储在数据库的任何位置。您可能需要调整此值,
以确保Slony或者Skytools等复制系统能够正确工作。如果是这样的话,应该可以从下游复制数据库的状态获得适当的值。
-l 参数通过指定下一个WAL段文件的名称,手动设置WAL启动位置。该选项使用WAL文件名,而不是LSN。下一个段的名字应该大于当前存在pg_wal目录下的任何WAL段文件名。
[postgres@lsl-test1 pg_commit_ts]$ cd ../pg_wal/ [postgres@lsl-test1 pg_wal]$ ls -l total 933892 drwx------. 2 postgres postgres 4096 Jul 4 06:15 archive_status -rw-------. 1 postgres postgres 16777216 Jul 17 10:49 000000010000005A000000FF -rw-------. 1 postgres postgres 16777216 Jul 17 10:49 000000010000005B0000001D -rw-------. 1 postgres postgres 16777216 Jul 17 10:49 000000010000005B00000026 -rw-------. 1 postgres postgres 16777216 Jul 17 10:49 000000010000005B00000028 -rw-------. 1 postgres postgres 16777216 Jul 17 10:50 000000010000005B00000025 -rw-------. 1 postgres postgres 16777216 Jul 17 10:51 000000010000005B00000003 -rw-------. 1 postgres postgres 16777216 Jul 17 10:52 000000010000005B00000004 -rw-------. 1 postgres postgres 16777216 Jul 17 10:52 000000010000005B0000002C -rw-------. 1 postgres postgres 16777216 Jul 17 10:55 000000010000005B0000002D -rw-------. 1 postgres postgres 16777216 Jul 17 14:27 000000010000005A000000F5
这些名称也是十六进制的,文件名包含三部分 ,第一部分时间线号(timeline ID) ,第二部分逻辑日志号 ,第三部分日志段号。
-m参数也是两个值,一个是下一个多事务号,一个是最旧的多事务号。
下一个多事务号的安全值,可以在目录pg_multixact/offsets查找数值最大的文件名,加1然后乘以65536(0x10000)。
最旧的多事务号的安全值可以通过查询目录下数值最小的文件名乘以65536。文件名都是十六进制。
[postgres@lsl-test1 pg_wal]$ ls -lrt /data/postgres/data/pg_multixact/offsets/ [postgres@lsl-test1 offsets]$ ls -l total 8 -rwx------. 1 postgres postgres 8192 May 17 18:04 0000
-o参数是设置下一个OID(OID,object 是pg内部使用,作为系统表的主键),我们恢复时可以不设置这个参数,因为设置一个超过数据库中最大值OID没有好的办法。
-O参数是设置下一个多事务偏移量。查找pg_multixact/members目录下数值最大的文件名,+1乘以52352 (0xCC80),可以计算出偏移量的安全值。目录下文件的文件名也是十六进制的。
[postgres@lsl-test1 offsets]$ ls -lrt /data/postgres/data/pg_multixact/members total 8 -rwx------. 1 postgres postgres 8192 May 17 17:22 0000 --wal-segsize参数设置新的WAL段大小 。 -x参数是手工设置下一个事务ID,pg_xact目录下可以查看数值最大的文件名,+1乘以 1048576 (0x100000),获取安全值。文件名也是十六进制。 [postgres@lsl-test1 offsets]$ ls -l /data/postgres/data/pg_xact/ total 16 -rwx------. 1 postgres postgres 8192 May 17 18:04 0000 -rw-------. 1 postgres postgres 8192 May 30 17:26 0001
PostgreSQL控制文件恢复测试
测试过程如下(基于PostgreSQL 11.2) :
1. 新建测试数据, 用到with oids的表, 因为OID无法确定, 看看是否会有异常
postgres=# create table lsl_oid1(id int primary key) with oids; CREATE TABLE postgres=# insert into lsl_oid1 select generate_series(1,100000); INSERT 0 100000 postgres=# select min(oid),max(oid) from lsl_oid1 ; min | max -------+-------- 16400 | 116399 (1 row)
2. 正常关闭数据,记录下控制文件信息
[postgres@lsl-test1 bin]$ /u01/postgres/pgsql/bin/pg_ctl stop -D /data/postgres/data waiting for server to shut down.... done server stopped ## 记下pg_controldata信息, 方便修复后进行比对 [postgres@lsl-test1 bin]$ /u01/postgres/pgsql/bin/pg_controldata -D /data/postgres/data pg_control version number: 1100 Catalog version number: 201809051 Database system identifier: 6691945724594983959 Database cluster state: shut down pg_control last modified: Thu 30 May 2019 05:26:41 PM CST Latest checkpoint location: 0/79E9888 Latest checkpoint's REDO location: 0/79E9888 Latest checkpoint's REDO WAL file: 000000010000000000000007 Latest checkpoint's TimeLineID: 1 Latest checkpoint's PrevTimeLineID: 1 Latest checkpoint's full_page_writes: on Latest checkpoint's NextXID: 0:1048585 Latest checkpoint's NextOID: 116400 Latest checkpoint's NextMultiXactId: 65536 Latest checkpoint's NextMultiOffset: 52352 Latest checkpoint's oldestXID: 561 Latest checkpoint's oldestXID's DB: 13878 Latest checkpoint's oldestActiveXID: 0 Latest checkpoint's oldestMultiXid: 1 Latest checkpoint's oldestMulti's DB: 13878 Latest checkpoint's oldestCommitTsXid:0 Latest checkpoint's newestCommitTsXid:0 Time of latest checkpoint: Thu 30 May 2019 05:26:40 PM CST Fake LSN counter for unlogged rels: 0/1 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_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 Float4 argument passing: by value Float8 argument passing: by value Data page checksum version: 0 Mock authentication nonce: 0000000000000000000000000000000000000000000000000000000000000000
3. 模拟控制文件故障,直接删除控制文件
[postgres@lsl-test1 global]$ pwd /pg/pg11/data/global [postgres@lsl-test1 global]$ rm -rf pg_control
4. 启动数据库,模拟控制文件丢失场景(正常关闭数据及处理方法)
[postgres@lsl-test1 global]$ /usr/pgsql-11/bin/pg_ctl start waiting for server to start....postgres: could not find the database system Expected to find it in the directory "/pg/pg11/data", but could not open file "/pg/pg11/data/global/pg_control": No such file or directory stopped waiting pg_ctl: could not start server Examine the log output.
下面开始正式重建控制文件,使得数据库可以正常启动。
5. 首先创建一个名为pg_control的空文件
[postgres@lsl-test1 global]$ touch $PGDATA/global/pg_control
6. 使用pg_resetwal修复pg_control
确认pg_resetwal参数值。 首先确认-c参数,上面参数详细分析发现目录下为空,因此暂时忽略此参数。 -e参数是设置下一个事务号的新纪元,我们测试环境没有其它复制系统因此也可以忽略。 -l参数,查看pg_wal下文件文件名,大于文件名最大值即可。 [postgres@lsl-test1 ~]$ cd /data/postgres/data/pg_wal [postgres@lsl-test1 pg_wal]$ ls -l 因此-m可以取0x10000,0x00000。 -o数不确定时,由于测试没有复制软件因此可以暂时忽略。 -O 查找pg_multixact/members目录下数值最大的文件名,+1乘以52352 (0xCC80)。 [postgres@lsl-test1 offsets]$ cd ../members/ [postgres@lsl-test1 members]$ ls -l total 8 -rwx------. 1 postgres postgres 8192 May 17 17:22 0000 因此-O=0xCC80。 -x参数查找pg_xact目录下可以查看数值最大的文件名,+1乘以 1048576 (0x100000)。 [postgres@lsl-test1 members]$ ls -lrt /data/postgres/data/pg_xact/ [postgres@lsl-test1 pg_xact]$ ls -l total 16 -rwx------. 1 postgres postgres 8192 May 17 18:04 0000 -rw-------. 1 postgres postgres 8192 May 30 17:26 0001 因此-x=0x200000。 不加-f参数时可以查看要写入控制文件中的参数内容。 [postgres@lsl-test1 pg_xact]$ /u01/postgres/pgsql/bin/pg_resetwal -l 00000001000000000000009E -m 0x10000,0x00001 -O 0xCC80 -x 0x100000 -D /data/postgres/data pg_resetwal: oldest multitransaction ID (-m) must not be 0 [postgres@lsl-test1 pg_xact]$ /u01/postgres/pgsql/bin/pg_resetwal -l 00000001000000000000009E -m 0x10000,0x00001 -O 0xCC80 -x 0x100000 -D /data/postgres/data pg_resetwal: pg_control exists but is broken or wrong version; ignoring it Guessed pg_control values: pg_control version number: 1100 Catalog version number: 201809051 Database system identifier: 6696828635748080009 Latest checkpoint's TimeLineID: 1 Latest checkpoint's full_page_writes: off Latest checkpoint's NextXID: 0:3 Latest checkpoint's NextOID: 10000 Latest checkpoint's NextMultiXactId: 1 Latest checkpoint's NextMultiOffset: 0 Latest checkpoint's oldestXID: 3 Latest checkpoint's oldestXID's DB: 0 Latest checkpoint's oldestActiveXID: 0 Latest checkpoint's oldestMultiXid: 1 Latest checkpoint's oldestMulti's DB: 0 Latest checkpoint's oldestCommitTsXid:0 Latest checkpoint's newestCommitTsXid:0 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 Float4 argument passing: by value Float8 argument passing: by value Data page checksum version: 0 Values to be changed: First log segment after reset: 000000010000000000000009 NextMultiXactId: 65536 OldestMultiXid: 1 OldestMulti's DB: 0 NextMultiOffset: 52352 NextXID: 2097152 OldestXID: 2297064448 OldestXID's DB: 0 If these values seem acceptable, use -f to force reset. [postgres@lsl-test1 pg_xact]$ cd ../global/ [postgres@lsl-test1 global]$ ls -l pg_control -rw-r--r--. 1 postgres postgres 0 May 30 17:36 pg_control
7. 启动数据库
确认控制文件参数无误后加上-f会写入到控制文件里。
[postgres@postgresdb global]$ /u01/postgres/pgsql/bin/pg_resetwal -l 00000001000000000000009E -m 0x10000,0x00001 -O 0xCC80 -x 0x100000 -D /data/postgres/data -f pg_resetwal: lock file "postmaster.pid" exists Is a server running? If not, delete the lock file and try again. [postgres@postgresdb global]$ ls 1136 1213_fsm 1232 1261 1262_vm 2671 2695 2847 2966_vm 4060 6001 6115 1136_fsm 1213_vm 1233 1261_fsm 2396 2672 2697 2964 2967 4060_vm 6002 pg_control 1136_vm 1214 1260 1261_vm 2396_fsm 2676 2698 2964_vm 3592 4061 6100 pg_control1 1137 1214_fsm 1260_fsm 1262 2396_vm 2677 2846 2965 3592_vm 6000 6100_vm pg_filenode.map 1213 1214_vm 1260_vm 1262_fsm 2397 2694 2846_vm 2966 3593 6000_vm 6114 pg_internal.init [postgres@postgresdb global]$ cd .. [postgres@postgresdb data]$ ls base pg_dynshmem pg_logical pg_replslot pg_stat pg_tblspc pg_wal postgresql.conf global pg_hba.conf pg_multixact pg_serial pg_stat_tmp pg_twophase pg_xact postmaster.opts pg_commit_ts pg_ident.conf pg_notify pg_snapshots pg_subtrans PG_VERSION postgresql.auto.conf postmaster.pid [postgres@postgresdb data]$ mv postmaster.pid postmaster.pid1 (非正常关闭需要关闭这个,正常关闭的不需要这个步骤) [postgres@postgresdb data]$ /u01/postgres/pgsql/bin/pg_resetwal -l 00000001000000000000009E -m 0x10000,0x00001 -O 0xCC80 -x 0x100000 -D /data/postgres/data -f pg_resetwal: pg_control exists but is broken or wrong version; ignoring it Write-ahead log reset [postgres@postgresdb data]$ /u01/postgres/pgsql/bin/pg_resetwal -l 000000010000005A000000F6 -m 0x10000,0x00001 -O 0xCC80 -x 0x100000 -D /data/postgres/data -f [postgres@lsl-test1 global]$ /u01/postgres/pgsql/bin/pg_resetwal -l 000000010000005A000000F6-m 0x10000,0x00001 -O 0xCC80 -x 0x100000 -D /data/postgres/data -f pg_resetwal: pg_control exists but is broken or wrong version; ignoring it Write-ahead log reset
启动数据库。
[postgres@lsl-test1 global]$ /u01/postgres/pgsql/bin/pg_ctl start -D /data/postgres/data waiting for server to start....2019-05-30 22:42:50.946 CST [2471] LOG: listening on IPv6 address "::1", port 5432 2019-05-30 22:42:50.946 CST [2471] LOG: listening on IPv4 address "127.0.0.1", port 5432 2019-05-30 22:42:50.949 CST [2471] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" 2019-05-30 22:42:50.961 CST [2471] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432" 2019-05-30 22:42:50.987 CST [2471] LOG: redirecting log output to logging collector process 2019-05-30 22:42:50.987 CST [2471] HINT: Future log output will appear in directory "log". done server started
8. 查看测试数据是否正常,然后插入新数据看数据库是否可用。
[postgres@lsl-test1 global]$ psql psql (11.2) Type "help" for help. postgres=# select min(oid),max(oid),count(*) from test_table1;
数据库可以正常访问。