用一個簡單的SQL語句將一個表或者整個資料庫恢復到以前的某一點
對於DBA們來說,這不是一件很熟悉的事嗎?John的選擇是什麼呢?
在Oracle資料庫10g之前,John可能會做一個時間點恢復,來將資料庫恢復至所希望的某一時刻的狀態。不幸的是,銀行的定期日常備份大約就是在那個時間開始,這就意味著他將不得不用接近24小時的有價值的歸案日誌來恢復資料庫。
Oracle9i資料庫中提供的另一種選擇是使用回閃查詢特性來重建到晚上11:00為止的該表的各行,並手動生成一組不同的表。這一方法儘管理論上可行,但如果表的數量很大,則會變得不切合實際。
幸運的是,John現在的資料庫是Oracle資料庫10g,所以他擁有更多的選擇。
回閃表
讓我們來看一下上面的情況。出問題的年末批處理可能僅影響少數表。例如,它可能僅用新的賬戶結餘更新了ACCOUNTS表。如果確實是這種情況,則John可以使用回閃表特性,它會將一個表恢復到一個過去的時間點的狀態。
執行回閃表操作不需要特殊的設定。唯一的要求是表必須具有可移動的行--或者是在建立表時設定,或者是在以後利用 ALTER TABLE ACCOUNTS ENABLE ROW MOVEMENT語句設定。FLASHBACK TABLE語句從撤消段中(undo segment)讀取該表的過去映像,並利用Oracle9i中引入的回閃查詢重建錶行。
如果模式所有者之外的非DBA使用者執行回閃表操作,她需要擁有對該表的SELECT、 DELETE、INSERT、ALTER和FLASHBACK許可權,或者擁有等價的ANY TABLE系統許可權。
對於John來說,ACCOUNTS像以下的樣子:
ACCOUNT_NO NUMBER(12),
BALANCE NUMBER(15,2)
STATUS CHAR(1)
STATUS的列值通常為A(active,活動),但是當利息計算應用程式啟動時,所有的賬戶均被凍結,此狀態也被更新為F(frozen,凍結)。當對每一賬戶都計算了利息後,該狀態被更新為I(interest applied,利息已計算)。
下面是John用使用回閃表特性的步驟:
他索要一個大概的時間點,在這一點上資料庫必須被重新檢查,答案是大約晚上11:00。
他定義一個期望返回到的邏輯參考點。下面是他現在查詢該表時所看到的內容:
select status, count(*)
from ACCOUNTS
group by status;
STATUS COUNT(*)
------ --------
I 27088
F 19999
該輸出顯示到目前為止已經有27 088個賬戶被處理(狀態=I)。 此前的狀態為A,這不是John所希望返回的位置。所希望的邏輯參考點應該是所有賬戶的狀態都為F的時間點。他必須恢復到所有賬戶狀態都為F的狀態,這大約發生於晚上11:00。
他檢查一個狀態=I的當前示例賬戶,以便為未來的校驗測試設定一個基線:
select account_no, balance
from ACCOUNTS
where status = I
and rownum < 2;
ACCOUNT_NO BALANCE
----------- --------
21633 3913.49
他發出這一語句將ACCOUNTS表恢復至那個時間:
flashback table ACCOUNTS to timestamp
to_timestamp (12/31/2003 23:00:00,mm/dd/yyyy hh24:mi:ss);
瞧! 整個表被恢復到那個時間的狀態了。只要撤消段中所儲存的撤消資料允許,John便可以回閃到過去的某個時間點。John也可以不用時間標記(stamp),而是使用系統改變數目(system change number,SCN),如下所示:
flashback table ACCOUNTS
to SCN 9988653338;
此表已被恢復至晚上11:00的狀態,但這是不是所希望的時間點狀態呢?John再次檢查狀態:
select status, count(*)
from ACCOUNTS
group by status;
STATUS COUNT(*)
------ --------
I 88
F 46999
仍然有88個記錄的狀態為T。所以,晚上11:00還不夠早,John不得不返回到更早的一個時間點。他將該表回閃到更早的一個時間點上--晚上10:30--然後再次檢查狀態:
flashback table ACCOUNTS to timestamp
to_timestamp (12/31/2003 22:30:00,mm/dd/yyyy hh24:mi:ss);
select status, count(*)
from ACCOUNTS
group by status;
STATUS COUNT(*)
------ --------
A 47087
John返回到的過去時間太遠了些,所有賬戶的狀態都為A,這是F狀態之前的一個狀態。於是,他不得不向後滾動一段時間,滾動到晚上10:45。
flashback table ACCOUNTS to timestamp
to_timestamp (12/31/2003 22:45:00,mm/dd/yyyy hh24:mi:ss);
select status, count(*)
from ACCOUNTS
group by status;
STATUS COUNT(*)
------ --------
F 47087
這正是他希望的時間點。他通過選擇他以前查詢過的賬戶來確證這一點。
select balance
from ACCOUNTS
where account_no = 21633;
BALANCE
-------
3836.75
有效的結餘為$3,913.49;返回到了晚上10:45的狀態。至此操作完成。可以向前和向後應用任意次回閃操作,直到到達所期望的準確時間點的狀態為止。
由於此表永遠不會被刪除,所以所有的從屬物件--如索引、約束條件、觸發器等等--都會保持完好無損。引用此表的所有獨立物件,如過程等,也都保持有效。即使是對分割表的全域性索引也會保留,並保持有效。
除了ACCOUNTS表之外,如果John還希望將TXN表回閃,那麼他可以應用許多由逗號隔開的表名,如下所示:
flashback table BANK.ACCOUNTS, bank.txn to scn 1234567;
通過一個功能強大的SQL語句完成了整個回閃表操作。
讓我們來研究另一種情況。假設Laura意外地刪除了一個關鍵字查詢表--GL_MASTER。意識到這一錯誤後,她問John能否恢復這個表。在 Oracle資料庫的以前一些版本中,這需要進行時間點恢復。但在Oracle資料庫10g中,刪除一個表只是將該表重新命名後放置到稱作回收站的邏輯容器中。
為了恢復此表,John只發出了以下命令:
flashback table gl_master to before drop;
這個表馬上就重新出現了,不需要任何恢復操作。請注意,與前面所描述的回閃操作不同,它不需要通過撤消段重建資料;而只是從回收站中將此表取回。
回閃資料庫
如果說邏輯上的破壞不限於為數不多的幾個表,那麼利用Oracle 10g中的Flashback Database(回閃資料庫)特性執行資料庫時間點恢復可以使恢復更快一些。Flashback Database不需要利用撤消空間(undo space),而是利用硬碟上被稱作快閃恢復區(flash recovery area)的另一區,它與撤消操作無關。Flashback Database允許進行資料庫時間點恢復,而不要求你首先恢復你資料庫的一個備份。
要回閃整個資料庫,John必須進行以下操作為資料庫做準備,從而使其具有回閃能力:
通過設定以下兩個引數來配置一個大小為2GB的回閃區域:
db_recovery_file_dest = /usr/users/oracle/10.1/recovery_area
db_recovery_file_dest_size = 2G
使用以下引數,以分鐘為單位配置最大回閃時間:
db_flashback_retention_target = 1440
這一數值反映出可以返回的最大時間;實際可能的時間由快閃恢復區域的可用空間大小來決定。所以這三個引數都是動態的,並可以使用ALTER SYSTEM命令進行設定。
使資料庫能夠回閃。為此,它必須處於archivelog模式下。在MOUNT階段,在OPEN之前,John發出如下命令:
alter database flashback on;
在啟動Flashback Database之後,它定期將已發生變化的塊寫入一個特殊型別的稱作回閃日誌的日誌檔案中。這些日誌不是由傳統的Log Writer (LGWR) 過程寫入,而是由一種稱作Recovery Writer (RVWR)的新過程寫入。與常規的重做日誌(redo logs)不同,回閃日誌既不需要由DBA建立,也不需要由他們維護;它們由Oracle Managed Files(OMF)自動在快閃恢復區域所指定的目錄中建立。這些檔案不會歸檔,所以,如果在該目錄發生介質故障後就不可能再進行恢復。
回閃操作
在所描述的Acme銀行的事例中,John認為在他所處的情況下回閃表不太可行。他不得不將整個資料庫回滾到過去的某個時間點。他再一次選擇晚上11:00作為開始點,併發出以下命令:
flashback database to timestamp
to_timestamp (12/31/2003
23:00:00,mm/dd/yyyy hh24:mi:ss);
這一操作執行所有的必要任務,如使