PostgreSQL恢復(fù)誤刪數(shù)據(jù)

在Oracle中爸黄;刪除表或者誤刪表記錄滞伟;有個(gè)閃回特性揭鳞,不需要停機(jī)操作炕贵,可以完美找回記錄。當(dāng)然也有一些其他的恢復(fù)工具野崇;例如odu工具称开,gdul工具。都可以找回?cái)?shù)據(jù)乓梨。而PostgreSQL目前沒(méi)有閃回特性鳖轰。如何在不停機(jī)情況下恢復(fù)誤刪數(shù)據(jù)。還好是有完整的熱備份扶镀。
本文描述的方法是:利用熱備份在另一臺(tái)服務(wù)器進(jìn)行數(shù)據(jù)恢復(fù)蕴侣;再導(dǎo)入正式環(huán)境;這樣不影響數(shù)據(jù)庫(kù)操作臭觉。這方法也適用在Oracle恢復(fù)昆雀。必須滿足幾個(gè)條件

  • 有完整的基礎(chǔ)數(shù)據(jù)文件備份和歸檔文件備份.所以備份是很重要的。
  • 有一臺(tái)裝好同款Postgres軟件的服務(wù)器

實(shí)例模擬講解

過(guò)程模擬誤刪表tbl_lottu_drop后蝠筑;后續(xù)進(jìn)行dml/ddl操作狞膘;表明正式數(shù)據(jù)庫(kù)還是進(jìn)行正常工作。在另外一臺(tái)數(shù)據(jù)庫(kù)基于數(shù)據(jù)庫(kù)PITR恢復(fù)什乙⊥旆猓恢復(fù)表tbl_lottu_drop的數(shù)據(jù)。

  • Postgres201 : 線上數(shù)據(jù)庫(kù)服務(wù)器
  • Postgres202 : 操作服務(wù)器

1.創(chuàng)建一個(gè)有效的備份

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.模擬誤操作

2.1創(chuàng)建一個(gè)需要恢復(fù)對(duì)象表tbl_lottu_drop臣镣。并插入1000記錄辅愿。也保證數(shù)據(jù)從數(shù)據(jù)緩存寫入磁盤中。

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這個(gè)獲取一個(gè)時(shí)間:用于后面基于數(shù)據(jù)庫(kù)PITR恢復(fù)當(dāng)然現(xiàn)實(shí)操作后只能記住一個(gè)大概的時(shí)間忆某;還往往是不準(zhǔn)点待;可能記住的時(shí)間是誤操作之后。后面有講解如何獲取需要恢復(fù)到那個(gè)時(shí)間點(diǎn)

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進(jìn)行drop表

lottu=# drop table tbl_lottu_drop;
DROP TABLE

2.4 后續(xù)進(jìn)行dml/ddl操作褒繁;表明正式數(shù)據(jù)庫(kù)還是進(jìn)行正常工作

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. 恢復(fù)操作

3.1 將備份拷貝到Postgres202數(shù)據(jù)庫(kù)上

[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數(shù)據(jù)庫(kù)上pg_xlog目錄下亦鳞;從哪個(gè)日志開(kāi)始拷貝

[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開(kāi)始到正在寫入的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 啟動(dòng)數(shù)據(jù)庫(kù)燕差;并驗(yàn)證數(shù)據(jù)

[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)

從這看數(shù)據(jù)是恢復(fù)了遭笋;copy到線上數(shù)據(jù)庫(kù)操作略

延伸點(diǎn)

下面講解下如何找到誤操作的時(shí)間徒探。即recovery_target_time = '2018-04-28 20:47:31.617808+08'的時(shí)間點(diǎn)瓦呼。上文是前面已經(jīng)獲取的;

  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
  1. 從lottu.log中可以找到這段日志央串;
[postgres@Postgres202 lottu]$ oid2name -d lottu -t tbl_lottu_drop
From database "lottu":
  Filenode      Table Name
--------------------------
     32784  tbl_lottu_drop  
#根據(jù)“32784”日志可以看到是表tbl_lottu_drop在2018-04-28 20:46:37.718442插入1000條記錄(所以恢復(fù)時(shí)間點(diǎn)選2018-04-28 20:47:31.617808+08沒(méi)毛病);即也是在事務(wù)id為1690操作的碗啄。并在事務(wù)id為1691進(jìn)行刪除操作质和。
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

根據(jù)“32784”日志可以看到是表tbl_lottu_drop在2018-04-28 20:46:37.718442插入1000條記錄所以恢復(fù)時(shí)間點(diǎn)選2018-04-28 20:47:31.617808+08沒(méi)毛病;即也是在事務(wù)id為1690操作的稚字。并在事務(wù)id為1691進(jìn)行刪除操作

  1. 所以上面的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'
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饲宿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胆描,更是在濱河造成了極大的恐慌瘫想,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昌讲,死亡現(xiàn)場(chǎng)離奇詭異国夜,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)短绸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門车吹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鸠按,你說(shuō)我怎么就攤上這事礼搁。” “怎么了目尖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵馒吴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我瑟曲,道長(zhǎng)饮戳,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任洞拨,我火速辦了婚禮扯罐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烦衣。我一直安慰自己歹河,他們只是感情好掩浙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著秸歧,像睡著了一般厨姚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上键菱,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天谬墙,我揣著相機(jī)與錄音,去河邊找鬼经备。 笑死拭抬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的侵蒙。 我是一名探鬼主播造虎,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蘑志!你這毒婦竟也來(lái)了累奈?” 一聲冷哼從身側(cè)響起贬派,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤急但,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后搞乏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體波桩,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年请敦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了镐躲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡侍筛,死狀恐怖萤皂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情匣椰,我是刑警寧澤裆熙,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站禽笑,受9級(jí)特大地震影響入录,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜佳镜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一僚稿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蟀伸,春花似錦蚀同、人聲如沸缅刽。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拷恨。三九已至,卻和暖如春谢肾,著一層夾襖步出監(jiān)牢的瞬間腕侄,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工芦疏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冕杠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓酸茴,卻偏偏與公主長(zhǎng)得像分预,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子薪捍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容