您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關怎么在PostgreSQL中恢復誤刪的數據,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
1、創建一個有效的備份
Postgres201 : 線上數據庫服務器 Postgres202 : 操作服務器 postgres=# select pg_start_backup(now()::text); pg_start_backup ----------------- 0/F000060 (1 row) [postgres@Postgres201 ~]$ rsync -acvz -L --exclude "pg_xlog" --exclude "pg_log" $PGDATA /data/backup/20180428 postgres=# select pg_stop_backup(); NOTICE: pg_stop_backup complete, all required WAL segments have been archived pg_stop_backup ---------------- 0/F000168 (1 row)
2.1 創建一個需要恢復對象表tbl_lottu_drop。并插入1000記錄。也保證數據從數據緩存寫入磁盤中。
lottu=> create table tbl_lottu_drop (id int); CREATE TABLE lottu=> insert into tbl_lottu_drop select generate_series(1,1000); INSERT 0 1000 lottu=> \c lottu postgres You are now connected to database "lottu" as user "postgres".
2.2 這個獲取一個時間:用于后面基于數據庫PITR恢復(當然現實操作后只能記住一個大概的時間;還往往是不準;可能記住的時間是誤操作之后。后面有講解如何獲取需要恢復到那個時間點)
lottu=# select now(); now ------------------------------- 2018-04-28 20:47:31.617808+08 (1 row) lottu=# checkpoint; CHECKPOINT lottu=# select pg_xlogfile_name(pg_switch_xlog()); pg_xlogfile_name -------------------------- 000000030000000000000010 (1 row)
2.3 進行drop表
lottu=# drop table tbl_lottu_drop; DROP TABLE
2.4 后續進行dml/ddl操作;表明正式數據庫還是進行正常工作
lottu=# create table tbl_lottu_log (id int); CREATE TABLE lottu=# insert into tbl_lottu_log values (1),(2); INSERT 0 2 lottu=# checkpoint; CHECKPOINT lottu=# select pg_xlogfile_name(pg_switch_xlog()); pg_xlogfile_name -------------------------- 000000030000000000000011 (1 row)
3.1 將備份拷貝到Postgres202數據庫上
[postgres@Postgres201 20180428]$ cd /data/backup/20180428 [postgres@Postgres201 20180428]$ ll total 4 drwx------. 18 postgres postgres 4096 Apr 28 20:42 data [postgres@Postgres201 20180428]$ rsync -acvz -L data postgres@192.168.1.202:/data/postgres
3.2 刪除不必要的文件
[postgres@Postgres202 data]$ cd $PGDATA [postgres@Postgres202 data]$ rm backup_label.old postmaster.pid tablespace_map.old
3.3 還原備份表空間軟鏈接
[postgres@Postgres202 data]$ cat tablespace_map 16385 /data/pg_data/lottu [postgres@Postgres202 data]$ mkdir -p /data/pg_data [postgres@Postgres202 data]$ cd pg_tblspc/ [postgres@Postgres202 pg_tblspc]$ mv 16385/ /data/pg_data/lottu [postgres@Postgres202 pg_tblspc]$ ln -s /data/pg_data/lottu ./16385 [postgres@Postgres202 pg_tblspc]$ ll total 0 lrwxrwxrwx. 1 postgres postgres 19 Apr 28 23:12 16385 -> /data/pg_data/lottu
3.4 將wal日志拷貝到Postgres202數據庫上pg_xlog目錄下;從哪個日志開始拷貝?
[postgres@Postgres202 data]$ mkdir -p pg_xlog/archive_status [postgres@Postgres202 data]$ cat backup_label START WAL LOCATION: 0/F000060 (file 00000003000000000000000F) CHECKPOINT LOCATION: 0/F000098 BACKUP METHOD: pg_start_backup BACKUP FROM: master START TIME: 2018-04-28 20:42:15 CST LABEL: 2018-04-28 20:42:13.244358+08
查看backup_label;知道00000003000000000000000F開始到正在寫入的wal日志。
[postgres@Postgres202 pg_xlog]$ ll total 65540 -rw-------. 1 postgres postgres 16777216 Apr 28 20:42 00000003000000000000000F -rw-------. 1 postgres postgres 313 Apr 28 20:42 00000003000000000000000F.00000060.backup -rw-------. 1 postgres postgres 16777216 Apr 28 20:48 000000030000000000000010 -rw-------. 1 postgres postgres 16777216 Apr 28 20:50 000000030000000000000011 -rw-------. 1 postgres postgres 16777216 Apr 28 20:55 000000030000000000000012
3.5 編輯recovery.conf文件
[postgres@Postgres202 data]$ vi recovery.conf restore_command = 'cp /data/arch/%f %p' # e.g. 'cp /mnt/server/archivedir/%f %p' recovery_target_time = '2018-04-28 20:47:31.617808+08' recovery_target_inclusive = false recovery_target_timeline = 'latest'
3.6 啟動數據庫;并驗證數據
[postgres@Postgres202 data]$ pg_start server starting [postgres@Postgres202 data]$ ps -ef | grep postgres root 1098 1083 0 22:32 pts/0 00:00:00 su - postgres postgres 1099 1098 0 22:32 pts/0 00:00:00 -bash root 1210 1195 0 22:55 pts/1 00:00:00 su - postgres postgres 1211 1210 0 22:55 pts/1 00:00:00 -bash postgres 1442 1 1 23:16 pts/0 00:00:00 /opt/pgsql96/bin/postgres postgres 1450 1442 0 23:16 ? 00:00:00 postgres: checkpointer process postgres 1451 1442 0 23:16 ? 00:00:00 postgres: writer process postgres 1459 1442 0 23:16 ? 00:00:00 postgres: wal writer process postgres 1460 1442 0 23:16 ? 00:00:00 postgres: autovacuum launcher process postgres 1461 1442 0 23:16 ? 00:00:00 postgres: archiver process last was 00000005.history postgres 1462 1442 0 23:16 ? 00:00:00 postgres: stats collector process postgres 1464 1099 0 23:16 pts/0 00:00:00 ps -ef postgres 1465 1099 0 23:16 pts/0 00:00:00 grep postgres [postgres@Postgres202 data]$ psql psql (9.6.0) Type "help" for help. postgres=# \c lottu lottu You are now connected to database "lottu" as user "lottu". lottu=> \dt List of relations Schema | Name | Type | Owner --------+----------------+-------+------- public | pitr_test | table | lottu public | tbl_lottu_drop | table | lottu lottu=> select count(1) from tbl_lottu_drop; count ------- 1000 (1 row)
從這看數據是恢復了;copy到線上數據庫操作略。
下面講解下如何找到誤操作的時間。即recovery_target_time = '2018-04-28 20:47:31.617808+08'的時間點。上文是前面已經獲取的;
1. 用pg_xlogdump解析這段日志。
[postgres@Postgres201 pg_xlog]$ pg_xlogdump -b 00000003000000000000000F 000000030000000000000012 > lottu.log pg_xlogdump: FATAL: error in WAL record at 0/12000648: invalid record length at 0/12000680: wanted 24, got 0
2. 從lottu.log中可以找到這段日志
rmgr: Transaction len (rec/tot): 8/ 34, tx: 1689, lsn: 0/100244A0, prev 0/10024460, desc: COMMIT 2018-04-28 20:45:49.736013 CST rmgr: Standby len (rec/tot): 24/ 50, tx: 0, lsn: 0/100244C8, prev 0/100244A0, desc: RUNNING_XACTS nextXid 1690 latestCompletedXid 1689 oldestRunningXid 1690 rmgr: Heap len (rec/tot): 3/ 3130, tx: 1690, lsn: 0/10024500, prev 0/100244C8, desc: INSERT off 9 blkref #0: rel 16385/16386/2619 fork main blk 15 (FPW); hole: offset: 60, length: 5116 rmgr: Btree len (rec/tot): 2/ 7793, tx: 1690, lsn: 0/10025140, prev 0/10024500, desc: INSERT_LEAF off 385 blkref #0: rel 16385/16386/2696 fork main blk 1 (FPW); hole: offset: 1564, length: 452 rmgr: Heap len (rec/tot): 2/ 184, tx: 1690, lsn: 0/10026FD0, prev 0/10025140, desc: INPLACE off 16 blkref #0: rel 16385/16386/1259 fork main blk 0 rmgr: Transaction len (rec/tot): 88/ 114, tx: 1690, lsn: 0/10027088, prev 0/10026FD0, desc: COMMIT 2018-04-28 20:46:37.718442 CST; inval msgs: catcache 49 catcache 45 catcache 44 relcache 32784 rmgr: Standby len (rec/tot): 24/ 50, tx: 0, lsn: 0/10027100, prev 0/10027088, desc: RUNNING_XACTS nextXid 1691 latestCompletedXid 1690 oldestRunningXid 1691 rmgr: Standby len (rec/tot): 24/ 50, tx: 0, lsn: 0/10027138, prev 0/10027100, desc: RUNNING_XACTS nextXid 1691 latestCompletedXid 1690 oldestRunningXid 1691 rmgr: XLOG len (rec/tot): 80/ 106, tx: 0, lsn: 0/10027170, prev 0/10027138, desc: CHECKPOINT_ONLINE redo 0/10027138; tli 3; prev tli 3; fpw true; xid 0:1691; oid 40976; multi 1; offset 0; oldest xid 1668 in DB 1; oldest multi 1 in DB 1; oldest/newest commit timestamp xid: 0/0; oldest running xid 1691; online rmgr: Standby len (rec/tot): 24/ 50, tx: 0, lsn: 0/100271E0, prev 0/10027170, desc: RUNNING_XACTS nextXid 1691 latestCompletedXid 1690 oldestRunningXid 1691 rmgr: Standby len (rec/tot): 24/ 50, tx: 0, lsn: 0/10027218, prev 0/100271E0, desc: RUNNING_XACTS nextXid 1691 latestCompletedXid 1690 oldestRunningXid 1691 rmgr: XLOG len (rec/tot): 80/ 106, tx: 0, lsn: 0/10027250, prev 0/10027218, desc: CHECKPOINT_ONLINE redo 0/10027218; tli 3; prev tli 3; fpw true; xid 0:1691; oid 40976; multi 1; offset 0; oldest xid 1668 in DB 1; oldest multi 1 in DB 1; oldest/newest commit timestamp xid: 0/0; oldest running xid 1691; online rmgr: XLOG len (rec/tot): 0/ 24, tx: 0, lsn: 0/100272C0, prev 0/10027250, desc: SWITCH rmgr: Standby len (rec/tot): 24/ 50, tx: 0, lsn: 0/11000028, prev 0/100272C0, desc: RUNNING_XACTS nextXid 1691 latestCompletedXid 1690 oldestRunningXid 1691 rmgr: Standby len (rec/tot): 16/ 42, tx: 1691, lsn: 0/11000060, prev 0/11000028, desc: LOCK xid 1691 db 16386 rel 32784 rmgr: Heap len (rec/tot): 8/ 2963, tx: 1691, lsn: 0/11000090, prev 0/11000060, desc: DELETE off 16 KEYS_UPDATED blkref #0: rel 16385/16386/1247 fork main blk 8 (FPW); hole: offset: 88, length: 5288
根據“32784”日志可以看到是表tbl_lottu_drop在2018-04-28 20:46:37.718442插入1000條記錄(所以恢復時間點選2018-04-28 20:47:31.617808+08沒毛病);即也是在事務id為1690操作的。并在事務id為1691進行刪除操作。
所以上面的recovery.conf 也可以改寫為:
restore_command = 'cp /data/arch/%f %p' # e.g. 'cp /mnt/server/archivedir/%f %p' recovery_target_xid = '1690' recovery_target_inclusive = false recovery_target_timeline = 'latest'
補充:PostgreSQL多種恢復實例分析
Postgresql歸檔恢復實例分析(時間線機制)
這篇文章根據實例介紹Postgresql歸檔恢復的方法,時間線的含義。
sed -ir "s/#*max_wal_senders.*/max_wal_senders = 10/" $PGDATA/postgresql.conf sed -ir "s/#*wal_level.*/wal_level = replica/" $PGDATA/postgresql.conf sed -ir "s/#*archive_mode.*/archive_mode = on/" $PGDATA/postgresql.conf sed -ir "s/#*archive_command.*/archive_command = 'test ! -f \${PGHOME}\/archive\/%f \&\& cp %p \${PGHOME}\/archive\/%f'/" $PGDATA/postgresql.conf
date;psql -c "create table test00 (id int primary key, info text)" Sat Apr 1 10:09:55 CST 2017 date;psql -c "insert into test00 values(generate_series(1,50000), repeat(md5(random()::text), 1000))" Sat Apr 1 10:10:10 CST 2017 date;psql -c "create table test01 (id int primary key, info text)" Sat Apr 1 10:10:48 CST 2017 date;psql -c "insert into test01 values(generate_series(1,50000), repeat(md5(random()::text), 1000))" Sat Apr 1 10:10:53 CST 2017
sed -ir "s/#*max_wal_senders.*/max_wal_senders = 10/" $PGDATA/postgresql.conf
配置pg_hba.conf通道
pg_basebackup -Fp -P -x -D ~/bin/data/pg_root21 -l basebackup21
date;psql -c "create table test02 (id int primary key, info text)" Sat Apr 1 10:15:59 CST 2017 date;psql -c "insert into test02 values(generate_series(1,100000), repeat(md5(random()::text), 1000))" Sat Apr 1 10:16:09 CST 2017
時間軸(第三行的縮寫代表Create Insert)
-10:09:55----10:10:10-----10:10:48-----10:10:53------10:15:59----10:16:09-- ----|-----------|------------|------------|-------------|-----------|------ C test00-----I test00-----C test01-----I test01-----C test01-----I test01--
情況1
沒有設置archive_timeout,整個數據目錄被刪除
rm -rf pg_root20/
cp -r pg_root21 pg_root20
修改pg_hba.conf阻止用戶連接
cp $PGHOME/share/recovery.conf.sample ./recovery.conf sed -ir "s/#*restore_command.*/restore_command = 'cp \${PGHOME}\/archive\/%f %p'/" $PGDATA/recovery.conf
test02存在,但是其中的數據被認為是未提交事務,表為空(最后一個xlog文件的內容全部遺失了)。
情況2
設置archive_timeout,整個數據目錄被刪除,歸檔timeout為60s,在test02表數據灌入之后,xlog自動切換并歸檔
(sed -ir "s/#*archive_timeout.*/archive_timeout = 60/" $PGDATA/postgresql.conf)
rm -rf pg_root20/
cp -r pg_root21 pg_root20
修改pg_hba.conf阻止用戶連接
cp $PGHOME/share/recovery.conf.sample ./recovery.conf sed -ir "s/#*restore_command.*/restore_command = 'cp \${PGHOME}\/archive\/%f %p'/" $PGDATA/recovery.conf
test02存在,數據也存在(由于歸檔設置了超時切換,最后一個xlog會被歸檔)。
情況3(重要)
設置archive_timeout,根據估計時間點嘗試多次恢復,不能確定想恢復到具體哪個時間點,歸檔timeout為60s
(sed -ir "s/#*archive_timeout.*/archive_timeout = 60/" $PGDATA/postgresql.conf)
rm -rf pg_root20/
cp -r pg_root21 pg_root20
修改pg_hba.conf阻止用戶連接
cp $PGHOME/share/recovery.conf.sample ./recovery.conf sed -ir "s/#*restore_command.*/restore_command = 'cp \${PGHOME}\/archive\/%f %p'/" $PGDATA/recovery.conf
(1) recovery_target_time = ‘2017-4-1 10:09:47' (基礎備份時間之前)
這里------------------------------------------------------------------------ -10:09:55----10:10:10-----10:10:48-----10:10:53------10:15:59----10:16:09-- ----|-----------|------------|------------|---basebackup---|--------|------ C test00-----I test00-----C test01-----I test01-----C test02-----I test02--
結果:
恢復時間定到了基礎備份之前,所以這里會恢復到最早時間點:基礎備份點。
LOG: recovery stopping before commit of transaction 1175, time 2017-04-01 10:15:59.597495+08
注意:無法恢復到基礎備份之前的點,所以再做基礎備份時,請保證數據一致性。
(2) recovery_target_time = ‘2017-4-1 10:10:00' (基礎備份時間之前)
---------這里--------------------------------------------------------------- -10:09:55----10:10:10-----10:10:48-----10:10:53------10:15:59----10:16:09-- ----|-----------|------------|------------|---basebackup---|--------|------ C test00-----I test00-----C test01-----I test01-----C test02-----I test02--
結果:
恢復時間定到了基礎備份之前,所以這里會恢復到最早時間點:基礎備份點。
LOG: recovery stopping before commit of transaction 1175, time 2017-04-01 10:15:59.597495+08
注意:無法恢復到基礎備份之前的點,所以再做基礎備份時,請保證數據一致性。
(3) recovery_target_time = ‘2017-4-1 10:16:00'
-------------------------------------------------------------這里----------- -10:09:55----10:10:10-----10:10:48-----10:10:53------10:15:59----10:16:09-- ----|-----------|------------|------------|---basebackup---|--------|------ C test00-----I test00-----C test01-----I test01-----C test02-----I test02--
結果:
表test02存在,但沒有數據。說明如果時間在基礎備份點之后,可以恢復到任意時間點。恢復后會創建新時間線。
LOG: last completed transaction was at log time 2017-04-01 10:15:59.597495+08
(3.1) 在(3)的基礎上繼續進行恢復:recovery_target_time = ‘2017-4-1 10:17:00'
------------------------------------------------------------------------這里 -10:09:55----10:10:10-----10:10:48-----10:10:53------10:15:59----10:16:09-- ----|-----------|------------|------------|---basebackup---|--------|------ C test00-----I test00-----C test01-----I test01-----C test02-----I test02--
結果:
同3,這次恢復創建了一條新的時間線3,這條時間線上面進行恢復的話,數據庫會去archive里面去找時間線2的xlog,但是歸檔目錄中的日志應該都是時間線1的,所以會報錯找不到xlog。
cp: cannot stat pathto/archive/00000002000000000000000A': No such file or directory
注意: 根據上述結論,請在每次恢復時都使用原始歸檔文件,即如果嘗試再次恢復,請重新使用基礎備份進行恢復,不要在前一次恢復的基礎上繼續進行,否則由于時間線切換,會找不到歸檔文件。
壓縮的歸檔日志
sed -ir "s/#*archive_command.*/archive_command = 'gzip -c %p > \${PGHOME}\/archive\/%f.gz'/" $PGDATA/postgresql.conf sed -ir "s/#*restore_command.*/restore_command = 'gzip -d -c \${PGHOME}\/archive\/%f.gz > %p'/" $PGDATA/recovery.conf
Postgresql9.6手冊(彭煜瑋翻譯)
restore_command (string)
用于獲取 WAL 文件系列的一個已歸檔段的本地 shell 命令。這個參數是歸檔恢復所必需的,但是對于流復制是可選的。在該字符串中的任何%f會被替換為從歸檔中獲得的文件的名字,并且任何%p會被在服務器上的復制目標路徑名替換(該路徑名是相對于當前工作目錄的,即集簇的數據目錄)。任何%r會被包含上一個可用重啟點的文件的名字所替換。在那些必須被保留用于使得一次恢復變成可重啟的文件中,這個文件是其中最早的一個,因此這個信息可以被用來把歸檔截斷為支持從當前恢復重啟所需的最小值。%r通常只被溫備配置(見Section 26.2)所使用。要嵌入一個真正的%字符,需要寫成%%。很重要的一點是,該命令只有在成功時才返回一個為零的退出狀態。該命令將會被詢問不存在于歸檔中的文件名,當這樣被詢問時它必須返回非零。
recovery_target_time (timestamp)
這個參數指定恢復將進入的時間戳。
recovery_target_xid (string)
這個參數指定恢復將進入的事務 ID。記住雖然事務 ID 是在事務開始時順序分配的,但是事務可能以不同的數字順序完成。那些在指定事務之前(也可以包括該事務)提交的事務將被恢復。精確的停止點也受到recovery_target_inclusive的影響。
recovery_target_timeline (string)
指定恢復到一個特定的時間線中。默認值是沿著基礎備份建立時的當前時間線恢復。將這個參數設置為latest會恢復到該歸檔中能找到的最新的時間線,這在一個后備服務器中有用。除此之外,你只需要在復雜的重恢復情況下設置這個參數,在這種情況下你需要返回到一個狀態,該狀態本身是在一次時間點恢復之后到達的。相關討論見Section25.3.5
Postgresql9.6手冊(彭煜瑋翻譯)
將數據庫恢復到一個之前的時間點的能力帶來了一些復雜性,這和有關時間旅行和平行宇宙的科幻小說有些相似。例如,在數據庫的最初歷史中,假設你在周二晚上5:15時丟棄了一個關鍵表,但是一直到周三中午才意識到你的錯誤。不用苦惱,你取出你的備份,恢復到周二晚上5:14的時間點,并上線運行。在數據庫宇宙的這個歷史中,你從沒有丟棄該表。但是假設你后來意識到這并非一個好主意,并且想回到最初歷史中周三早上的某個時間。你沒法這樣做,在你的數據庫在線運行期間,它重寫了某些WAL段文件,而這些文件本來可以將你引向你希望回到的時間。因此,為了避免出現這種狀況,你需要將完成時間點恢復后生成的WAL記錄序列與初始數據庫歷史中產生的WAL記錄序列區分開來。
要解決這個問題,PostgreSQL有一個時間線概念。無論何時當一次歸檔恢復完成,一個新的時間線被創建來標識恢復之后生成的WAL記錄序列。時間線ID號是WAL段文件名的一部分,因此一個新的時間線不會重寫由之前的時間線生成的WAL數據。實際上可以歸檔很多不同的時間線。雖然這可能看起來是一個無用的特性,但是它常常扮演救命稻草的角色。考慮到你不太確定需要恢復到哪個時間點的情況,你可能不得不做多次時間點恢復嘗試和錯誤,直到最終找到從舊歷史中分支出去的最佳位置。如果沒有時間線,該處理將會很快生成一堆不可管理的混亂。而有了時間線,你可以恢復到任何之前的狀態,包括早先被你放棄的時間線分支中的狀態。
每次當一個新的時間線被創建,PostgreSQL會創建一個“時間線歷史”文件,它顯示了新時間線是什么時候從哪個時間線分支出來的。系統在從一個包含多個時間線的歸檔中恢復時,這些歷史文件對于允許系統選取正確的WAL段文件非常必要。因此,和WAL段文件相似,它們也要被歸檔到WAL歸檔區域。歷史文件是很小的文本文件,因此將它們無限期地保存起來的代價很小,而且也是很合適的(而段文件都很大)。如果你喜歡,你可以在一個歷史文件中增加注釋來記錄如何和為什么要創建該時間線。當你由于試驗的結果擁有了一大堆錯綜復雜的不同時間線時,這種注釋將會特別有價值。
恢復的默認行為是沿著相同的時間線進行恢復,該時間線是基礎備份創建時的當前時間線。如果你希望恢復到某個子女時間線(即,你希望回到在一次恢復嘗試后產生的某個狀態),你需要在recovery.conf中指定目標時間線ID。你不能恢復到早于該基礎備份之前分支出去的時間線。
關于怎么在PostgreSQL中恢復誤刪的數據就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。