在pg中,當我們進行了基于時間點的還原(PITR)后,數(shù)據(jù)庫會啟用新的時間線并繼續(xù)進行操作。
但是,當我們進行基于時間點的還原后如果發(fā)現(xiàn)又出現(xiàn)錯誤,想要繼續(xù)還原數(shù)據(jù)庫該如何操作呢?如何還原到原先舊的時間線呢?
我們可以使用recovery_target_timeline參數(shù)來指定數(shù)據(jù)庫還原到某一個時間線上。如果你還不清楚這個參數(shù)該如何使用,或者說壓根不知道時間線是啥,那么請繼續(xù)往下看。
每當我們在數(shù)據(jù)庫中完成一個事務(wù)時,所做的操作都會記錄到$PGDATA/pg_wal目錄下的wal日志文件中。
wal日志文件一般都是下面這種格式:
000000010000000000000001
當一個wal日志被寫滿后,便會創(chuàng)建新的wal日志000000010000000000000002,以此類推。
該文件中前8位,即:00000001表示的便是數(shù)據(jù)庫的時間線。
從控制文件中也可以看到:
-bash-4.1$-> pg_controldata |grep TimeLineID Latest checkpoint's TimeLineID: 1 Latest checkpoint's PrevTimeLineID: 1
每當我們進行基于時間點的還原后,時間線便會加1,并創(chuàng)建一個名為NewTimelineID.history的新文件。這個文件是干什么用的我們后面會介紹。
recovery_target_timeline是一個參數(shù),它可以幫助我們將集群帶入歷史記錄中的任何時間線,只要有效的基本備份和所有存檔日志都到位。
我們來看看下面的例子:
首先,重新初始化一個新的數(shù)據(jù)庫集群。
-bash-4.1$-> ls pg_wal 000000010000000000000001 archive_status
然后創(chuàng)建一張表并插入數(shù)據(jù)。
bill=# create table timeline(tid int, remarks varchar(1000)); CREATE TABLE bill=# insert into timeline values('1','This is timeline id 1'); INSERT 0 1 bill=# checkpoint; CHECKPOINT bill=# select pg_switch_wal(); pg_switch_wal --------------- 0/15D4B70 (1 row)
剛剛插入的數(shù)據(jù)便記錄在000000010000000000000001的wal日志中。
當wal日志寫到000000010000000000000005時,進行一次完整的備份,接著再產(chǎn)生一些新的wal日志。
-bash-4.1$ ls -rlt total 147460 -rw------- 1 postgres postgres 16777216 Nov 22 13:03 000000010000000000000001 -rw------- 1 postgres postgres 16777216 Nov 22 13:03 000000010000000000000002 -rw------- 1 postgres postgres 16777216 Nov 22 13:03 000000010000000000000003 -rw------- 1 postgres postgres 16777216 Nov 22 13:05 000000010000000000000004 -rw------- 1 postgres postgres 16777216 Nov 22 13:05 000000010000000000000005 -rw------- 1 postgres postgres 337 Nov 22 13:05 000000010000000000000005.00000028.backup -rw------- 1 postgres postgres 16777216 Nov 22 13:06 000000010000000000000006 -rw------- 1 postgres postgres 16777216 Nov 22 13:06 000000010000000000000007
可以看到,現(xiàn)在最新的wal日志是000000010000000000000008
接著插入一條新的數(shù)據(jù)。
bill=# insert into timeline values('1','This is timeline id 1 after basebackup'); INSERT 0 1 bill=# checkpoint; CHECKPOINT
-bash-4.1$ pg_waldump 000000010000000000000008 | grep INSERT rmgr: Heap len (rec/tot): 54/ 214, tx: 487, lsn: 0/08000110, prev 0/080000D8, desc: INSERT off 2 flags 0x00, blkref #0: rel 1663/13530/16384 blk 0 FPW
然后再產(chǎn)生幾個wal日志,現(xiàn)在的情況如下:
-bash-4.1$ ls -rlt total 311308 -rw------- 1 16777216 Nov 22 13:03 000000010000000000000001 -rw------- 1 16777216 Nov 22 13:03 000000010000000000000002 -rw------- 1 16777216 Nov 22 13:03 000000010000000000000003 -rw------- 1 16777216 Nov 22 13:05 000000010000000000000004 -rw------- 1 16777216 Nov 22 13:05 000000010000000000000005 -rw------- 1 337 Nov 22 13:05 000000010000000000000005.00000028.backup -rw------- 1 16777216 Nov 22 13:06 000000010000000000000006 -rw------- 1 16777216 Nov 22 13:06 000000010000000000000007 -rw------- 1 16777216 Nov 22 13:07 000000010000000000000008 -rw------- 1 16777216 Nov 22 13:07 000000010000000000000009 -rw------- 1 16777216 Nov 22 13:09 00000001000000000000000A
如下圖所示:
此時,在我插入第二條數(shù)據(jù)前,我想要把數(shù)據(jù)還原到000000010000000000000007這個點。
因此我在postgresql.conf文件中將恢復(fù)目標lsn設(shè)置為“ 0/07000060”。
接著進行還原,當我們還原之后,數(shù)據(jù)庫切換到了新的時間線。
除此之外還有哪些改變呢?
恢復(fù)結(jié)束是指數(shù)據(jù)庫打開進行寫入的點。
創(chuàng)建了新的時間線的 history file文件,如00000002.history。
前一個時間線上的部分WAL文件已被新時間線的ID復(fù)制。
檢查點記錄寫在新的時間線上。
日志中會記錄下列信息:
LOG: starting point-in-time recovery to WAL location (LSN) "0/7000060" LOG: restored log file "000000010000000000000005" from archive LOG: redo starts at 0/5000028 LOG: consistent recovery state reached at 0/5000138 LOG: database system is ready to accept read only connections LOG: restored log file "000000010000000000000006" from archive LOG: restored log file "000000010000000000000007" from archive LOG: recovery stopping after WAL location (LSN) "0/7000060" LOG: pausing at the end of recovery HINT: Execute pg_wal_replay_resume() to promote.
此時,PostgreSQL已在wal日志7處分支到新的時間線,并開始創(chuàng)建時間線ID為2的新wal日志。我們可以下wal日志目錄下看到00000002.history文件。
該文件是可讀文件,內(nèi)容大致為:
1parentTLI> 0/70000D8 switchpoint> after LSN 0/7000060reason> parentTLI ID of the parent timeline switchpoint XLogRecPtr of the WAL location where the switch happened reason human-readable explanation of why the timeline was changed
接下來,我向wal日志00000002000000000000000A (0/A000060)中插入新的數(shù)據(jù)。
bill=# insert into timeline values('2','This is timeline id 2 correct'); INSERT 0 1
以及另一個wal日志00000002000000000000000D(0/D000000)中插入另一條數(shù)據(jù)。
bill=# insert into timeline values('2','This is timeline id 2 wrong at 0/D000000'); INSERT 0 1
這個時候,我在00000002000000000000000D的wal日志中執(zhí)行了錯誤的操作,想要回退到時間線2的00000002000000000000000C處,那么我要如何操作呢,如果像前面一樣只指定lsn那么怎么保證不會回退到時間線1中呢?
這個時候我們便可以通過指定recovery_target_timeline來實現(xiàn)。
在postgresql.conf文件中添加:
recovery_target_timeline = '2' recovery_target_lsn = '0/0C000060'
接著,啟動數(shù)據(jù)庫,可以看到日志中:
LOG: database system was interrupted; last known up at 2020-11-22 13:05:01 IST LOG: restored log file "span style="color: rgb(255, 0, 0);" data-mce-style="color: #ff0000;">00000002.history/span>" from archive cp: cannot stat `/u02/archivelogs/00000003.history': No such file or directory LOG: starting point-in-time recovery to WAL location (LSN) "0/C000060" LOG: restored log file "00000002.history" from archive LOG: restored log file "span style="color: rgb(255, 0, 0);" data-mce-style="color: #ff0000;">000000010000000000000005/span>" from archive LOG: redo starts at 0/5000028 LOG: consistent recovery state reached at 0/5000138 LOG: database system is ready to accept read only connections LOG: restored log file "000000010000000000000006" from archive LOG: restored log file "000000020000000000000007" from archive LOG: restored log file "000000020000000000000008" from archive LOG: restored log file "000000020000000000000009" from archive LOG: restored log file "00000002000000000000000A" from archive LOG: restored log file "00000002000000000000000B" from archive LOG: restored log file "span style="color: rgb(255, 0, 0);" data-mce-style="color: #ff0000;">00000002000000000000000C/span>" from archive LOG: recovery stopping after WAL location (LSN) "span style="color: rgb(255, 0, 0);" data-mce-style="color: #ff0000;">0/C000060/span>" LOG: pausing at the end of recovery HINT: Execute pg_wal_replay_resume() to promote. .. LOG: redo done at 0/C000060 LOG: last completed transaction was at log time 2020-11-22 13:15:29.696929+05:30
然后查詢該表驗證:
bill=# select * from timeline; tid | remarks -----+------------------------------- 1 | This is timeline id 1 2 | This is timeline id 2 correct (2 rows)
此時可以看到新建了00000003.history文件,該文件內(nèi)容如下:
-bash-4.1$ cat 00000003.history 1 0/70000D8 after LSN 0/7000060 2 0/C0000D8 after LSN 0/C000060
我們不難發(fā)現(xiàn):
history file這個文件中記錄的就是這個時間線是從哪個WAL位置開始生成的。
補充:PostgreSQL promote過程 和 一主多備 時間線 無縫對接 詳解
PostgreSQL的physical standby數(shù)據(jù)庫的promote過程,數(shù)據(jù)庫會在pg_xlog目錄產(chǎn)生3個文件。
例如將備庫1 promote,它將在pg_xlog目錄產(chǎn)生如下文件:
A.partial (xlog) NEWTL_A (xlog) NEWTL.history (history file)
例如備庫1當前已接收到的XLOG位置是 00000001000000000000002D 文件中的某個位置 0/2D15D7D0,現(xiàn)在promote它 。
將會在pg_xlog目錄中產(chǎn)生3個文件:
00000001000000000000002D.partial 00000002000000000000002D (00000001000000000000002D.partial 的內(nèi)容會拷貝到 00000002000000000000002D) 00000002.history 1 0/2D15D7D0 no recovery target specified
假設(shè)還有一個備庫叫備庫2,備庫2如何能順利的對接到已激活的備庫1呢?
有個前提條件
備庫2在TL1這條時間線上,還沒有接收到00000001000000000000002D 這個文件。
把00000002.history拷貝到備庫2的pg_xlog。
備庫2會在應(yīng)用完00000001000000000000002C后請求下一個時間線的 00000002000000000000002D 文件。
這樣就能完美對接。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。