(一)問(wèn)題背景
最近在生產(chǎn)環(huán)境中,開(kāi)發(fā)人員誤操作,使用truncate將oracle數據庫某個(gè)表的數據全部刪除了,在刪除之后,開(kāi)發(fā)人員發(fā)現自己闖禍了,于是聯(lián)系值班的DBA進(jìn)行緊急數據恢復。
經(jīng)過(guò)分析,表被truncate后,使用一般的閃回表、閃回查詢(xún)、閃回事物等方法,是不可能將數據找回來(lái)的,可以使用閃回數據庫、閃回數據歸檔的方法來(lái)進(jìn)行恢復,但是通常在生產(chǎn)環(huán)境中,都不會(huì )開(kāi)啟這2個(gè)特性,所以剩下的只有使用RMAN進(jìn)行數據恢復了。
對于使用RMAN進(jìn)行數據恢復,可以在生產(chǎn)環(huán)境上直接進(jìn)行,也可以恢復到其它機器上。
直接在生產(chǎn)環(huán)境上恢復:①需要停止生產(chǎn)數據庫;②數據庫需要保持一致性,比如說(shuō),我需要將數據庫恢復到12:00,那么數據庫中其他表的數據也將恢復到12點(diǎn),有可能會(huì )丟失較多數據;③如果恢復過(guò)程中出現其它問(wèn)題也比較麻煩,耽誤了生產(chǎn)業(yè)務(wù)執行。
恢復到其它機器上:②不需要停生產(chǎn)庫;②僅僅丟失truncate表的數據,比如說(shuō),我需要將數據庫恢復到12:00,那么我只需將整個(gè)庫在測試環(huán)境上恢復到12點(diǎn),再將我們丟失表的數據通過(guò)DB_LINK或數據泵等方式恢復到生產(chǎn)環(huán)境,生產(chǎn)環(huán)境其它表的數據是不受影響的;③恢復失敗,并不會(huì )影響到生產(chǎn)庫。
所以,經(jīng)過(guò)一番考慮,決定將數據庫恢復到其它機器上,然后再將truncate表的數據導回到生產(chǎn)環(huán)境。
此次恢復操作是同事做的,在恢復過(guò)程中,由于流程不熟悉,查資料耽誤了一些時(shí)間(大約20分鐘),雖然數據庫恢復完成了,但沒(méi)有達到快速恢復的要求。思考了一下,假如自己來(lái)做,能否在開(kāi)發(fā)人員焦急等待的情況下,自己毫不慌亂、快速穩定的完成數據庫恢復?確實(shí)是不可能的。一方面恢復流程不熟練,畢竟數據庫恢復操作一年也不可能遇到幾次,另一方面在用戶(hù)及開(kāi)發(fā)人員催促的情況下,DBA也很容易慌張,影響效率。因此最好的方式是:提前演練、寫(xiě)好操作流程。當故障發(fā)生時(shí),照著(zhù)文檔操作,以最快的速度恢復生產(chǎn)。
(二)環(huán)境準備
生產(chǎn)環(huán)境異機環(huán)境
操作系統RedHat6.7RedHat6.7
數據庫版本11.2.0.4(RAC,2個(gè)節點(diǎn))11.2.0.4(單節點(diǎn))
db_nameprodbprodb
instance_nameprodb1、prodb2prodb
數據庫安裝情況安裝GI+數據庫軟件+創(chuàng )建數據庫安裝GI+數據庫軟件 (不用創(chuàng )建數據庫)
磁盤(pán)組信息OCR : 3*1GB,normal
DATA :3*5GB,external
ARCH : 1*5GB,externalOCR : 3*1GB,normal
DATA :3*5GB,external
ARCH : 1*5GB,external
備注:為了方便,在后續環(huán)境中,生產(chǎn)環(huán)境數據庫簡(jiǎn)稱(chēng)“生產(chǎn)庫”,異機環(huán)境的數據庫簡(jiǎn)稱(chēng)“測試庫”。
(三)測試方案
(四)詳細執行過(guò)程
(4.1)創(chuàng )建測試表
這里創(chuàng )建了2個(gè)測試表,作用分別如下:
lijiaman.test01:用于做truncate測試的表,最后在測試庫需要進(jìn)行test01表的恢復。
lijiaman.test02:用于模擬數據庫事務(wù),對該表不斷執行插入操作,使得數據庫產(chǎn)生大量歸檔日志。
(Ⅰ)表test01,一共有14筆數據。
SQL> CREATE TABLE test01 AS SELECT * FROM scott.emp;Table createdSQL> select count(*) from test01; COUNT(*)---------- 14
(ⅠⅠ)表test02,持續往里面寫(xiě)入數據
--創(chuàng )建表test02create table test02( col1 number, col2 number, col3 varchar2(30), col4 date, col5 varchar2(100) );--創(chuàng )建隨機數據插入存儲過(guò)程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,沒(méi)隔30s執行一次上面的存儲過(guò)程declarejob1 number;beginsys.dbms_job.submit(job => job1,what => 'p_insert_test02;',next_date => sysdate,interval => 'sysdate + 30/(1440*60)'); --每隔30s向test02表插入10000筆隨機數據commit;end;/
(4.2)對數據庫進(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
確認歸檔日志備份情況,可以看到,本次全備份歸檔日志備份到了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)數據庫正常運行,產(chǎn)生大量歸檔
由于日志序列號是遞增的(以resetlogs打開(kāi)數據庫例外),因此查詢(xún)每個(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;
結果為:
即thread1上的歸檔日志最大序列號為67,thread2上的歸檔日志最大序列號為48(這里因為個(gè)人電腦開(kāi)較多虛擬機太卡,所以只開(kāi)了節點(diǎn)1,節點(diǎn)2就沒(méi)有日志生成,并不影響本次實(shí)驗結果的準確性)。
(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)數據庫正常運行,產(chǎn)生大量歸檔
上一次備份到了sequence=57的日志,上一次備份后又生成了25個(gè)日志,這里生成大量日志是為了模擬生產(chǎn)數據庫這次交易的情況。
(4.6)開(kāi)發(fā)人員發(fā)現表數據被truncate
開(kāi)發(fā)人員發(fā)現程序報錯,查看表test01,發(fā)現數據全沒(méi)了,開(kāi)發(fā)人員確認數據被自己刪除(假設)。
(4.7)DBA執行異機恢復
思路整理:
本次恢復,需要將test01表恢復到truncate之前,我們需要有執行truncate操作之前的數據庫全備和歸檔備份。第一次全備歸檔日志文件之備份到了thread1=57,thread2=48,在執行全被之后,又生成了許多的日志文件,我們要將數據庫恢復到truncate之前(這里以我們記錄的時(shí)間2019-09-27 19:37:31 為恢復點(diǎn)),那么我們還需要新的日志來(lái)做恢復,需要的日志如下:
thread1:日志57~67肯定需要,日志67~82不一定需要;
thread2:由于節點(diǎn)未開(kāi)啟,不需要日志來(lái)做恢復。
step1:將生產(chǎn)庫的備份集傳到測試庫
[oracle@node1 databaseBackup]$ scp * 192.168.10.66:/databaseBackup/
step2:對恢復需要的歸檔日志進(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:根據生產(chǎn)庫的pfile,構造一個(gè)測試庫的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:將數據庫添加到HA中,以便可以使用ASM存儲
[oracle@test ~]$ srvctl add database -d prodb -o /u01/app/oracle/product/11.2.0/db_1
step6:恢復控制文件,修改pfile文件,重新啟動(dòng)數據庫到mount狀態(tài)
RMAN> restore controlfile from "/databaseBackup/controlfile_0lucr7m2_1_1";
注意:此時(shí)存在一個(gè)問(wèn)題,我們在構造pfile文件的時(shí)候,里面填寫(xiě)了控制文件的位置,這個(gè)位置是生產(chǎn)庫上的位置,我們執行控制文件恢復后,需要對參數文件中的control_files參數進(jì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
--接下來(lái),修改pfile文件的control_files參數
[oracle@test ~]$ cd $ORACLE_HOME/dbs[oracle@test dbs]$ vim initprodb.ora # 改control_files位置control_files='+data/prodb/controlfile/current.256.1020114329'
--重啟數據庫到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:注冊新的歸檔日志備份集到備庫的控制文件中
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:恢復數據庫到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:確認數據是已經(jīng)否恢復回來(lái)
--先以只讀方式打開(kāi)數據庫,如果有問(wèn)題,還可以重新執行恢復SQL> alter database open read only;Database altered.--確認數據是否找回來(lái)SQL> select count(*) from lijiaman.test01; COUNT(*)---------- 14
step10:如果沒(méi)問(wèn)題,關(guān)閉數據庫,以resetlogs方式打開(kāi)
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.
恢復完成。
(4.8)將恢復的數據導入到生產(chǎn)環(huán)境
可以使用expdp/impdp或者是dblink將數據從測試庫導入到生產(chǎn)庫中。
【完】