(一)問題背景
最近在生產(chǎn)環(huán)境中,開發(fā)人員誤操作,使用truncate將oracle數(shù)據(jù)庫某個(gè)表的數(shù)據(jù)全部刪除了,在刪除之后,開發(fā)人員發(fā)現(xiàn)自己闖禍了,于是聯(lián)系值班的DBA進(jìn)行緊急數(shù)據(jù)恢復(fù)。
經(jīng)過分析,表被truncate后,使用一般的閃回表、閃回查詢、閃回事物等方法,是不可能將數(shù)據(jù)找回來的,可以使用閃回?cái)?shù)據(jù)庫、閃回?cái)?shù)據(jù)歸檔的方法來進(jìn)行恢復(fù),但是通常在生產(chǎn)環(huán)境中,都不會(huì)開啟這2個(gè)特性,所以剩下的只有使用RMAN進(jìn)行數(shù)據(jù)恢復(fù)了。
對(duì)于使用RMAN進(jìn)行數(shù)據(jù)恢復(fù),可以在生產(chǎn)環(huán)境上直接進(jìn)行,也可以恢復(fù)到其它機(jī)器上。
直接在生產(chǎn)環(huán)境上恢復(fù):①需要停止生產(chǎn)數(shù)據(jù)庫;②數(shù)據(jù)庫需要保持一致性,比如說,我需要將數(shù)據(jù)庫恢復(fù)到12:00,那么數(shù)據(jù)庫中其他表的數(shù)據(jù)也將恢復(fù)到12點(diǎn),有可能會(huì)丟失較多數(shù)據(jù);③如果恢復(fù)過程中出現(xiàn)其它問題也比較麻煩,耽誤了生產(chǎn)業(yè)務(wù)執(zhí)行。
恢復(fù)到其它機(jī)器上:②不需要停生產(chǎn)庫;②僅僅丟失truncate表的數(shù)據(jù),比如說,我需要將數(shù)據(jù)庫恢復(fù)到12:00,那么我只需將整個(gè)庫在測(cè)試環(huán)境上恢復(fù)到12點(diǎn),再將我們丟失表的數(shù)據(jù)通過DB_LINK或數(shù)據(jù)泵等方式恢復(fù)到生產(chǎn)環(huán)境,生產(chǎn)環(huán)境其它表的數(shù)據(jù)是不受影響的;③恢復(fù)失敗,并不會(huì)影響到生產(chǎn)庫。
所以,經(jīng)過一番考慮,決定將數(shù)據(jù)庫恢復(fù)到其它機(jī)器上,然后再將truncate表的數(shù)據(jù)導(dǎo)回到生產(chǎn)環(huán)境。
此次恢復(fù)操作是同事做的,在恢復(fù)過程中,由于流程不熟悉,查資料耽誤了一些時(shí)間(大約20分鐘),雖然數(shù)據(jù)庫恢復(fù)完成了,但沒有達(dá)到快速恢復(fù)的要求。思考了一下,假如自己來做,能否在開發(fā)人員焦急等待的情況下,自己毫不慌亂、快速穩(wěn)定的完成數(shù)據(jù)庫恢復(fù)?確實(shí)是不可能的。一方面恢復(fù)流程不熟練,畢竟數(shù)據(jù)庫恢復(fù)操作一年也不可能遇到幾次,另一方面在用戶及開發(fā)人員催促的情況下,DBA也很容易慌張,影響效率。因此最好的方式是:提前演練、寫好操作流程。當(dāng)故障發(fā)生時(shí),照著文檔操作,以最快的速度恢復(fù)生產(chǎn)。
(二)環(huán)境準(zhǔn)備
生產(chǎn)環(huán)境異機(jī)環(huán)境
操作系統(tǒng)RedHat6.7RedHat6.7
數(shù)據(jù)庫版本11.2.0.4(RAC,2個(gè)節(jié)點(diǎn))11.2.0.4(單節(jié)點(diǎn))
db_nameprodbprodb
instance_nameprodb1、prodb2prodb
數(shù)據(jù)庫安裝情況安裝GI+數(shù)據(jù)庫軟件+創(chuàng)建數(shù)據(jù)庫安裝GI+數(shù)據(jù)庫軟件 (不用創(chuàng)建數(shù)據(jù)庫)
磁盤組信息OCR : 3*1GB,normal
DATA :3*5GB,external
ARCH : 1*5GB,externalOCR : 3*1GB,normal
DATA :3*5GB,external
ARCH : 1*5GB,external
備注:為了方便,在后續(xù)環(huán)境中,生產(chǎn)環(huán)境數(shù)據(jù)庫簡稱“生產(chǎn)庫”,異機(jī)環(huán)境的數(shù)據(jù)庫簡稱“測(cè)試庫”。
(三)測(cè)試方案
(四)詳細(xì)執(zhí)行過程
(4.1)創(chuàng)建測(cè)試表
這里創(chuàng)建了2個(gè)測(cè)試表,作用分別如下:
lijiaman.test01:用于做truncate測(cè)試的表,最后在測(cè)試庫需要進(jìn)行test01表的恢復(fù)。
lijiaman.test02:用于模擬數(shù)據(jù)庫事務(wù),對(duì)該表不斷執(zhí)行插入操作,使得數(shù)據(jù)庫產(chǎn)生大量歸檔日志。
(Ⅰ)表test01,一共有14筆數(shù)據(jù)。
SQL> CREATE TABLE test01 AS SELECT * FROM scott.emp;Table createdSQL> select count(*) from test01; COUNT(*)---------- 14
(ⅠⅠ)表test02,持續(xù)往里面寫入數(shù)據(jù)
--創(chuàng)建表test02create table test02( col1 number, col2 number, col3 varchar2(30), col4 date, col5 varchar2(100) );--創(chuàng)建隨機(jī)數(shù)據(jù)插入存儲(chǔ)過程create or replace procedure p_insert_test02 isBEGIN FOR i IN 1..10000 LOOP insert into test02(col1,col2,col3,col4,col5) values ((select round(dbms_random.value(1, 100000000)) from dual), (select round(dbms_random.value(10000, 100000000)) from dual), (select dbms_random.string('a', 25) from dual), sysdate, (select dbms_random.string('a', 85) from dual)); commit; END LOOP;end p_insert_test02;--制定job,沒隔30s執(zhí)行一次上面的存儲(chǔ)過程declarejob1 number;beginsys.dbms_job.submit(job => job1,what => 'p_insert_test02;',next_date => sysdate,interval => 'sysdate + 30/(1440*60)'); --每隔30s向test02表插入10000筆隨機(jī)數(shù)據(jù)commit;end;/
(4.2)對(duì)數(shù)據(jù)庫進(jìn)行完全備份
rman target /RMAN> run {allocate channel c1 type disk;allocate channel c2 type disk;sql' alter system archive log current';backup database format '/databaseBackup/full_db_%U';sql' alter system archive log current';backup archivelog all format '/databaseBackup/archlog_%U';backup current controlfile format '/databaseBackup/controlfile_%U';backup spfile format '/databaseBackup/spfile_%U';release channel c1;release channel c2;}
生成的備份集如下:
[oracle@node1 databaseBackup]$ ls -l
total 4136752
-rw-r----- 1 oracle asmadmin 1451128832 Sep 27 19:27 archlog_0iucr7hg_1_1
-rw-r----- 1 oracle asmadmin 1462116352 Sep 27 19:27 archlog_0jucr7hh_1_1
-rw-r----- 1 oracle asmadmin 1406464 Sep 27 19:27 archlog_0kucr7lr_1_1
-rw-r----- 1 oracle asmadmin 18841600 Sep 27 19:28 controlfile_0lucr7m2_1_1
-rw-r----- 1 oracle asmadmin 805953536 Sep 27 19:25 full_db_0eucr7f7_1_1
-rw-r----- 1 oracle asmadmin 477528064 Sep 27 19:25 full_db_0fucr7f7_1_1
-rw-r----- 1 oracle asmadmin 18841600 Sep 27 19:25 full_db_0gucr7h3_1_1
-rw-r----- 1 oracle asmadmin 98304 Sep 27 19:25 full_db_0hucr7ha_1_1
-rw-r----- 1 oracle asmadmin 98304 Sep 27 19:28 spfile_0mucr7m5_1_1
確認(rèn)歸檔日志備份情況,可以看到,本次全備份歸檔日志備份到了thread1:57,thread2:48。
RMAN> list archivelog all;
List of Archived Log Copies for database with db_unique_name PRODB
=====================================================================
Key Thrd Seq S Low Time
------- ---- ------- - ---------
3 1 6 A 24-SEP-19
Name: +ARCH/prodb/archivelog/2019_09_24/thread_1_seq_6.258.1019832847
......
100 1 57 A 27-SEP-19
Name: +ARCH/prodb/archivelog/2019_09_27/thread_1_seq_57.355.1020108489
1 2 1 A 24-SEP-19
Name: +ARCH/prodb/archivelog/2019_09_24/thread_2_seq_1.256.1019830885
......
80 2 48 A 24-SEP-19
Name: +ARCH/prodb/archivelog/2019_09_24/thread_2_seq_48.335.1019838555
(4.3)數(shù)據(jù)庫正常運(yùn)行,產(chǎn)生大量歸檔
由于日志序列號(hào)是遞增的(以resetlogs打開數(shù)據(jù)庫例外),因此查詢每個(gè)實(shí)例上生成的最大日志即可
SELECT *FROM (SELECT thread#, SEQUENCE#, NAME, ROW_NUMBER() OVER(PARTITION BY thread# ORDER BY SEQUENCE# DESC) rn FROM V$ARCHIVED_LOG)WHERE rn=1;
結(jié)果為:
即thread1上的歸檔日志最大序列號(hào)為67,thread2上的歸檔日志最大序列號(hào)為48(這里因?yàn)閭€(gè)人電腦開較多虛擬機(jī)太卡,所以只開了節(jié)點(diǎn)1,節(jié)點(diǎn)2就沒有日志生成,并不影響本次實(shí)驗(yàn)結(jié)果的準(zhǔn)確性)。
(4.4)模擬test01表被truncate,記下時(shí)間
SQL> select sysdate from dual;SYSDATE-------------------2019-09-27 19:37:31SQL> SQL> truncate table test01;Table truncated.
(4.5)數(shù)據(jù)庫正常運(yùn)行,產(chǎn)生大量歸檔
上一次備份到了sequence=57的日志,上一次備份后又生成了25個(gè)日志,這里生成大量日志是為了模擬生產(chǎn)數(shù)據(jù)庫這次交易的情況。
(4.6)開發(fā)人員發(fā)現(xiàn)表數(shù)據(jù)被truncate
開發(fā)人員發(fā)現(xiàn)程序報(bào)錯(cuò),查看表test01,發(fā)現(xiàn)數(shù)據(jù)全沒了,開發(fā)人員確認(rèn)數(shù)據(jù)被自己刪除(假設(shè))。
(4.7)DBA執(zhí)行異機(jī)恢復(fù)
思路整理:
本次恢復(fù),需要將test01表恢復(fù)到truncate之前,我們需要有執(zhí)行truncate操作之前的數(shù)據(jù)庫全備和歸檔備份。第一次全備歸檔日志文件之備份到了thread1=57,thread2=48,在執(zhí)行全被之后,又生成了許多的日志文件,我們要將數(shù)據(jù)庫恢復(fù)到truncate之前(這里以我們記錄的時(shí)間2019-09-27 19:37:31 為恢復(fù)點(diǎn)),那么我們還需要新的日志來做恢復(fù),需要的日志如下:
thread1:日志57~67肯定需要,日志67~82不一定需要;
thread2:由于節(jié)點(diǎn)未開啟,不需要日志來做恢復(fù)。
step1:將生產(chǎn)庫的備份集傳到測(cè)試庫
[oracle@node1 databaseBackup]$ scp * 192.168.10.66:/databaseBackup/
step2:對(duì)恢復(fù)需要的歸檔日志進(jìn)行再次備份,得到缺少的歸檔日志
run {allocate channel c1 type disk;sql' alter system archive log current';backup archivelog all format '/databaseBackup/archlog_20190927_%U';release channel c1;}
得到的歸檔日志備份集如下:
-rw-r----- 1 oracle asmadmin 1621476864 Sep 27 20:50 archlog_20190927_0nucrcd2_1_1
-rw-r----- 1 oracle asmadmin 1643560960 Sep 27 20:51 archlog_20190927_0oucrcg5_1_1
-rw-r----- 1 oracle asmadmin 1581030912 Sep 27 20:53 archlog_20190927_0pucrcjj_1_1
傳送到備庫上
[oracle@node1 databaseBackup]$ scp archlog_20190927_0* 192.168.10.66:/databaseBackup/
step3:根據(jù)生產(chǎn)庫的pfile,構(gòu)造一個(gè)測(cè)試庫的pfile
[oracle@test dbs]$ pwd/u01/app/oracle/product/11.2.0/db_1/dbs[oracle@test dbs]$ vim init initprodb.ora# 添加如下信息audit_file_dest='/u01/app/oracle/admin/prodb/adump'audit_trail='db'compatible='11.2.0.4.0'control_files='+DATA/prodb/controlfile/current.260.1019830577'db_block_size=8192db_create_file_dest='+DATA'db_domain=''db_name='prodb'diagnostic_dest='/u01/app/oracle'dispatchers='(PROTOCOL=TCP) (SERVICE=prodbXDB)'enable_ddl_logging=TRUElog_archive_dest_1='LOCATION=+arch'log_archive_format='%t_%s_%r.dbf'open_cursors=300pga_aggregate_target=399507456processes=200remote_login_passwordfile='exclusive'sessions=225sga_target=1199570944prodb.undo_tablespace='UNDOTBS1'[oracle@test dbs]$ lshc_prodb.dat hc_testdb1.dat hc_testdb.dat init.ora initprodb.ora lkTESTDB
創(chuàng)建pfile里面涉及到的路徑:
[oracle@test ~]$ mkdir -p /u01/app/oracle/admin/prodb/adump
step4:將備庫啟動(dòng)到nomount狀態(tài)
[oracle@test ~]$ export ORACLE_SID=prodb[oracle@test ~]$ sqlplus / as sysdbaSQL*Plus: Release 11.2.0.4.0 Production on Fri Sep 27 20:58:15 2019Copyright (c) 1982, 2013, Oracle. All rights reserved.Connected to an idle instance.SQL> startup nomount;ORACLE instance started.Total System Global Area 1202556928 bytesFixed Size 2252704 bytesVariable Size 402653280 bytesDatabase Buffers 788529152 bytesRedo Buffers 9121792 bytesSQL>
step5:將數(shù)據(jù)庫添加到HA中,以便可以使用ASM存儲(chǔ)
[oracle@test ~]$ srvctl add database -d prodb -o /u01/app/oracle/product/11.2.0/db_1
step6:恢復(fù)控制文件,修改pfile文件,重新啟動(dòng)數(shù)據(jù)庫到mount狀態(tài)
RMAN> restore controlfile from "/databaseBackup/controlfile_0lucr7m2_1_1";
注意:此時(shí)存在一個(gè)問題,我們?cè)跇?gòu)造pfile文件的時(shí)候,里面填寫了控制文件的位置,這個(gè)位置是生產(chǎn)庫上的位置,我們執(zhí)行控制文件恢復(fù)后,需要對(duì)參數(shù)文件中的control_files參數(shù)進(jìn)行修改,修改方法如下:
--首先,確認(rèn)contril file在asm中的位置,
ASMCMD> pwd+data/prodb/controlfileASMCMD> ls -ltType Redund Striped Time Sys NameCONTROLFILE UNPROT FINE SEP 27 21:00:00 Y current.256.1020114329
--接下來,修改pfile文件的control_files參數(shù)
[oracle@test ~]$ cd $ORACLE_HOME/dbs[oracle@test dbs]$ vim initprodb.ora # 改control_files位置control_files='+data/prodb/controlfile/current.256.1020114329'
--重啟數(shù)據(jù)庫到mount狀態(tài)
[oracle@test ~]$ sqlplus / as sysdbaSQL*Plus: Release 11.2.0.4.0 Production on Fri Sep 27 21:17:26 2019Copyright (c) 1982, 2013, Oracle. All rights reserved.Connected to:Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit ProductionWith the Partitioning, Automatic Storage Management, OLAP, Data Miningand Real Application Testing optionsSQL> shutdown immediateORA-01507: database not mountedORACLE instance shut down.SQL> startup mountORACLE instance started.Total System Global Area 1202556928 bytesFixed Size 2252704 bytesVariable Size 402653280 bytesDatabase Buffers 788529152 bytesRedo Buffers 9121792 bytesDatabase mounted.SQL>
step7:注冊(cè)新的歸檔日志備份集到備庫的控制文件中
RMAN> catalog backuppiece "/databaseBackup/archlog_20190927_0nucrcd2_1_1";RMAN> catalog backuppiece "/databaseBackup/archlog_20190927_0oucrcg5_1_1";RMAN> catalog backuppiece "/databaseBackup/archlog_20190927_0pucrcjj_1_1";
step8:恢復(fù)數(shù)據(jù)庫到truncate之前
RMAN>SQL"ALTER SESSION SET NLS_LANGUAGE=''AMERICAN''";RMAN>SQL"ALTER SESSION SET NLS_DATE_FORMAT=''YYYY-MM-DD HH24:MI:SS''";RUN{SET UNTIL TIME '2019-09-27 19:37:31';RESTORE DATABASE;RECOVER DATABASE;}
step9:確認(rèn)數(shù)據(jù)是已經(jīng)否恢復(fù)回來
--先以只讀方式打開數(shù)據(jù)庫,如果有問題,還可以重新執(zhí)行恢復(fù)SQL> alter database open read only;Database altered.--確認(rèn)數(shù)據(jù)是否找回來SQL> select count(*) from lijiaman.test01; COUNT(*)---------- 14
step10:如果沒問題,關(guān)閉數(shù)據(jù)庫,以resetlogs方式打開
SQL> shutdown immediate;Database closed.Database dismounted.ORACLE instance shut down.SQL> startup mountORACLE instance started.Total System Global Area 1202556928 bytesFixed Size 2252704 bytesVariable Size 402653280 bytesDatabase Buffers 788529152 bytesRedo Buffers 9121792 bytesDatabase mounted.SQL> alter database open resetlogs;Database altered.
恢復(fù)完成。
(4.8)將恢復(fù)的數(shù)據(jù)導(dǎo)入到生產(chǎn)環(huán)境
可以使用expdp/impdp或者是dblink將數(shù)據(jù)從測(cè)試庫導(dǎo)入到生產(chǎn)庫中。
【完】