一天在公司與幾位負責緊急救援電話支持服務的同事聊天,當我詢問客戶主要有哪些求救電話時,他們告訴我最多的求救電話是兩類:一類是數據庫宕機或掛起,特別是RAC 數據庫出現宕機,另外一類則是數據庫壞塊問題。前者在我意料之中,而後者則有點出乎我的意料。但仔細一想,事實的確可能如此。大家千萬別小看數據壞塊的處理,從危害程度而言,幾個小小數據壞塊的確可能導致客戶核心數據不可訪問,甚至丟失。而從技術角度而言,數據壞塊處理涉及的內部機制和處理方法是非常複雜的。爲此,Oracle有若干專題文章是講述數據壞塊處理的。
本章我們先從若干案例介紹起,先讓大家從不同層面感觸一下數據庫壞塊處理的多樣性和複雜性,然後將詳細介紹壞塊的處理流程,壞塊的定位和相關解決方案,例如使用DBMS_REPAIR包或設置10231事件、ROWID掃描方法等。
可怕的數據庫壞塊
案例1:邏輯壞塊導致的壞塊
第一個案例來自於國稅某系統,筆者在《品悟性能優化》的第十七章曾經“繪聲繪色”地敘述過。本書再次摘要該案例主要內容如下:
- 故障現象和原因
首先,瞭解到共有18個數據壞塊,三個表核心業務表分別有一個數據壞塊,其他15個壞塊爲索引。
其次,瞭解硬件特別是存儲並沒有報錯,說明很可能是邏輯壞塊。
再其次,瞭解到前一天的RMAN備份順利完成,意味着RMAN備份集已經包含了數據邏輯壞塊。
- 處理方案和結果
針對上述具體情況,分別制定了不同的處理方案。
- 首先不能直接通過RMAN進行恢復,因爲備份集很可能已經包含了邏輯壞塊,如果恢復只能同樣恢復成壞塊。
- 針對索引,採取重建索引到新的數據文件的方案,並獲得了成功。
- 針對三個核心業務表,先採取了設置event=”10231 trace name context forever, level 10”,或者通過SKIP_CORRUPTION_BLOCKS方法,試圖將壞塊之外的數據讀出來,但都沒有成功。
- 與應用開發人員進一步溝通,發現其中一張表爲彙總統計表,於是採取了重新運行彙總程序的辦法,重新生成了該表並移到一個新的物理位置。
- 爲減少數據損失,對剩餘2張表採取了ROWID Range Scan技術。其中一個表獲得了成功,但1000多萬的表丟失了4條記錄。而另外一個表卻沒有成功。
- 針對最後一張表,只好採用了最後一個不得已的招術:Oracle的內部技術DUL。最終也基本獲得了成功,400多萬的表丟失了10多條記錄。
案例2:CPU損壞導致的壞塊
- 故障現象和原因
該案例來自於某省移動BOSS系統。具體故障現象表現爲:數據庫實例宕機,數據庫也異常關閉。在客戶請求Oracle緊急現場救援服務,Oracle工程師趕到現場之後,經過分析alert.log日誌發現:Online Redo Log出現壞塊,Oracle在發現Redo Log校驗失敗之後,爲保護數據,Oracle主動關閉數據庫實例和數據庫。
在硬件公司的積極配合下,後來發現導致該故障的最終原因是數據庫服務器的CPU損壞,導致Oracle的Online Redo Log被寫壞。
- 處理方案和結果
Oracle工程師在現場評估了問題原因和影響範圍之後,最終確定通過RMAN進行數據庫的不完全恢復,即只恢復到出現壞塊的Online Redo Log的前一個。該數據庫已經達到TB級,爲此花費了20多個小時才完成了該核心系統的不完全恢復。由於恢復期間,數據庫處於Mount狀態,意味着業務不得不停頓了20多個小時。更嚴重的是,由於是不完全恢復,導致丟失了一個多小時的業務數據。
事後,本人在與承擔具體搶救任務的同事聊天時,他坦言:其實他當時是恢復到出現壞塊的Online Redo Log的前兩個,而不是前一個。原因是他擔心前一個也有壞塊,而Oracle可能沒有及時檢查出來。如果出現這種不幸,那Oracle數據庫還是無法打開,可能要再次進行恢復。他說寧可客戶數據多丟一點,也要保證數據庫儘快順利地打開,及時恢復正常業務。他當時還很神祕地告訴我:千萬別告訴客戶哦,客戶還以爲只丟了一個日誌文件的數據呢。抱歉,哥兒們,今天在這兒泄露你的祕密了。呵呵。
案例3:Oracle Bug導致的壞塊
- 故障現象
2011年3月21日5:10分左右,某銀行系統出現異常,具體情況如下:
- 數據庫在預分配空間時出現異常,報空間分配失敗
- 賬務流水錶出現數據塊邏輯讀寫錯誤,不可讀寫,進而中斷交易。
- smon後臺進程不斷嘗試恢復損壞的數據塊,恢復後將實例2自動關閉。
- 處理方案和結果
Oracle公司工程師在分析故障原因之後,採取瞭如下措施:
- 關閉ORACLE數據庫一致性保護校驗(初始化參數:db_block_checksum改爲false)
- 刪除賬務流水錶
- 重建賬務流水錶
至10:05分,最終恢復了正常業務。
在事故處理過程中,該銀行還進行了恢復丟失的賬務流水數據、數據追加、數據庫多次全庫備份、數據一致性驗證等工作,在此不贅述。
- 事故原因分析
根據Oracle公司提交的故障處理報告,最終確定導致數據庫出現邏輯壞塊的根本原因是Bug所導致。
- 由故障引發的架構性改造
可見,上述數據壞塊故障導致了該銀行某系統約5小時的業務停頓。如何有效防範同類事故的發生,引起該銀行各級領導和技術人員高度關注,並確定在已經通過存儲鏡像技術建立同城容災系統基礎上,開展數據庫備援系統建設。爲此,確定了建設總目標如下:
- 備援系統將與生產系統建立在同一機房。即不是地理意義上的容災系統。
- 在發生類似上述數據壞塊故障時,並不是切換到容災系統。事實上,通過存儲鏡像技術建立的容災系統,無法防範數據壞塊的傳播。
- 在發生類似上述邏輯壞塊故障時,爲降低故障影響面,也不是將生產系統直接切換到備援系統。而是優先考慮通過備援系統,快速搶救和恢復被損壞數據。
- 如果故障影響面較大,才考慮將生產系統直接切換到備援系統。
壞塊處理主要流程
導致數據庫壞塊的原因
Oracle數據庫壞塊分類物理壞塊和邏輯壞塊。所謂物理壞塊是由於硬件I/O故障或操作系統故障而引起的數據塊寫入錯誤,而邏輯壞塊通常是Oracle軟件問題導致,具體爲數據塊頭信息被寫壞,導致頭信息與數據塊內容不匹配。可見,導致數據庫壞塊的原因很多,例如主機硬件故障、存儲硬件和軟件故障、操作系統故障、Oracle軟件故障等,甚至應用軟件壓力過大都可能導致數據庫出現壞塊。
但有一種壞塊現象則是正常現象。即當對某個數據對象以nologging方式實施了操作,例如“alter index <索引名> rebuild nologging”,而事後又對包含該對象的數據文件通過日誌進行了recover操作。這樣,該對象所對應的數據塊將被Oracle標識爲corrupt,當訪問這些壞塊時, Oracle將報ORA-1578錯誤。這種情況下,雖然可以通過下述方法查詢出哪些數據對象出現壞塊,但不僅無法通過recover恢復數據,也無法通過其它手段有效地從壞塊中搶救數據。客戶唯一能做的就是小心、小心,再小心,不要對nologging操作的數據對象進行recover操作!
壞塊處理主要流程
區區幾個數據庫壞塊,帶來的影響可能是致命的。如何提高壞塊處理效率,降低壞塊影響範圍?就象世界處理所有緊急突發事件一樣,一定要事先有預案和處理流程。以下就是Oracle公司提供的壞塊處理主要流程圖:
下面我們就按此流程圖展開更詳細的描述:
確定問題範圍
首先,一旦發現出現數據庫壞塊,應該記錄下有關壞塊的所有信息,包括alert.log文件和trace文件記錄的信息,確定壞塊涉及的範圍。例如應該評估是單個數據壞塊,還是因爲對nologging操作的數據對象進行recover操作之後引起的大量壞塊。
此時,Oracle建議最好能通過DBVERIFY工具對壞塊所在的數據文件和其它文件進行掃描,分析是否有更多壞塊的存在,從而更準確地確定問題範圍。如果我們獲取了詳細的數據文件/壞塊清單,我們就可有的放矢,顯著提高壞塊處理效率。
Oracle建議的一些最佳實踐經驗如下:
- 完整記錄原始的壞塊出錯信息,以及遇到壞塊的應用模塊信息。
- 將首次遇到壞塊的幾小時至當前時間的log信息抽取出來,單獨保存爲一個文件進行重點分析。
- 將log文件中涉及的trace 文件進行保存。
- 瞭解硬件和操作系統級是否存在報錯信息。
- 查詢硬件和存儲是否採用異步I/O(ASYNC I/O),磁盤快速寫(Fast Write Disk)等技術。
- 查詢當前Oracle備份信息,備份時間、備份類型、備份地點等。
- 查詢數據庫是否是歸檔或非歸檔模式。
檢查和替換有問題的硬件
通常而言,大部分壞塊是由於硬件故障而導致的。因此,在在進行壞塊數據修復之前,最好對硬件進行充分檢查,特別是當出現大量數據壞塊或者錯誤是偶發性的時候。而且,根據Oracle經驗,操作系統報錯可能會滯後,甚至即便操作系統檢查正常,也不代表硬件就一切正常。
因此,在壞塊數據修復之前,最好能將有故障或疑似有故障的硬件進行替換或修復。如有可能,最好將故障存儲設備的數據文件移到正常的存儲設備。具體步驟如下:
步驟如下:
- 將表空間設置爲offline狀態。例如:
SQL> alter tablespace userdata offline;
- 通過操作系統命令移動或複製數據文件。例如:
cp /u01/oradata/userdata01.dbf /u01/oradata/userdata01.dbf
或
mv /u01/oradata/userdata01.dbf /u01/oradata/userdata01.dbf
- 執行 alter tablespace rename datafile命令。例如:
ALTER TABLESPACE userdata RENAME DATAFILE
‘/u01/oradata/userdata01.dbf’
TO ‘/u01/oradata/userdata01.dbf’;
該命令只適合於移動非SYSTEM表空間的數據文件以及不包含活躍(active)undo、temporary的數據文件。
- 將表空間設置爲online狀態。例如:
SQL> alter tablespace userdata online;
- 可將原來的數據文件刪除。例如:
rm /u01/oradata/userdata01.dbf
確定壞塊影響的數據庫對象
接下來的第三個重要步驟就是確定壞塊影響的數據庫對象,該步驟同樣非常重要。在後面敘述壞塊恢復操作時,大家就會看到:不同類型數據庫對象的壞塊,處理方法是完全不同的。更重要的是:大家千萬別搞錯壞塊所在的數據文件,以及壞塊所包含的數據庫對象。否則,連恢復的數據庫對象都搞錯了,那可就是錯上加錯了。呵呵。
爲此,Oracle官方建議最好能制定如下一張壞塊信息表,以便進行壞塊信息記錄和處理過程跟蹤:
下面將詳細介紹該表格的各項內容及相關術語,以及信息獲取辦法:
- Original Error
即系統記錄的原始錯誤信息,包括ORA-1578 / ORA-1110 , ORA-600及該錯誤的相關參數等。
- Absolute File# &AFN和Relative File# &RFN
Absolute File#表示絕對文件號,簡稱&AFN,表示數據庫級的文件編號。而Relative File#表示相對文件號,簡稱&RFN,表示表空間級的文件編號。
通常而言, Oracle從8i開始,絕對文件號和相對文件號就是相同的了,除非數據庫是從Oracle 7版升級和遷移而來。通過如下語句可查詢出數據庫的所有表空間和對應的絕對文件號和相對文件號:
SELECT tablespace_name, file_id “AFN”, relative_fno “RFN” FROM dba_data_files;
- &FILENAME
即包含壞塊的數據文件名。
- &BL
即出現壞塊的數據塊編號。
- &TSN和&TABLESPACE_NAME
即出現壞塊的表空間編號和表空間名稱。
上述&AFN、&RFN、&FILENAME、&BL、&TSN和&TABLESPACE_NAME等信息,在出現壞塊的原始錯誤信息中均可獲取,例如,出錯信息如下:
ORA-01578: ORACLE data block corrupted (file # 7, block # 12698)
ORA-01110: data file 22: ‘/oracle1/oradata/V816/oradata/V816/users01.dbf’
則:
&AFN : “22” (from the ORA-1110 portion of the error)
&RFN : “7” (from the “file #” in the ORA-1578)
&BL : “12698” (from the “block #” in the ORA-1578)
&FILENAME: ‘/oracle1/oradata/V816/oradata/V816/users01.dbf’
&TSN、&TABLESPACE_NAME 則通過如下語句獲取:
SELECT ts# “TSN” FROM v$datafile WHERE file#=&AFN;
SELECT tablespace_name FROM dba_data_files WHERE file_id=&AFN;
&TS_BLOCK_SIZE可通過如下語句獲取:
SELECT block_size FROM dba_tablespaces WHERE tablespace_name = (SELECT tablespace_name FROM dba_data_files WHERE file_id=&AFN);
另外,如果v$datafile視圖不包括出錯的&AFN號,並且&AFN號大於DB_FILES初始化參數,則該文件應該是TEMPFILE。爲此,可通過如下語句查詢出該文件名:
SELECT name FROM v$tempfile WHERE file#=(&AFN – &DB_FILES_value);
也能通過如下語句,查詢出TEMPFILE的&AFN號和&RFN號:
SELECT tablespace_name, file_id+value “AFN”, relative_fno “RFN”
FROM dba_temp_files, v$parameter
WHERE name=’db_files’;
- Segment Type,Segment Owner, Segment Name
即壞塊的數據段類型、屬主名和數據段名稱,基於&AFN和&BL值,通過如下語句可查詢出這些信息:
SELECT tablespace_name, segment_type, owner, segment_name
FROM dba_extents
WHERE file_id = &AFN
and &BL between block_id AND block_id + blocks – 1;
如果壞塊是TEMPFILE,則上述查詢的Segment Type值將爲“TEMPORARY”,其它字段將爲空。
如果上述查詢沒有返回記錄,則壞塊就位於本地化管理表空間(Locally Managed Tablespace,簡稱LMT)的段頭(Segment Header)。針對這種情況,Oracle不僅會在alert.log中進行記錄,而且通過如下語句可獲得進一步信息:
SELECT owner, segment_name, segment_type, partition_name
FROM dba_segments
WHERE header_file = &AFN
and header_block = &BL;
- Related Object,Recovery Options
即相關數據對象名稱和恢復可選方案。限於篇幅,我們僅羅列常見類型的數據對象的處理:
- TABLE
若壞塊所在的數據對象爲SYS用戶下的數據字典表,建議聯繫Oracle技術支持部門。此時,整個數據庫通常需要進行恢復。
若壞塊位於普通表或分區,則查詢相關索引信息:
SELECT owner, index_name, index_type
FROM dba_indexes
WHERE table_owner=’&OWNER’ AND table_name=’&SEGMENT_NAME’;
並進一步確定該表是否主鍵存在:
SELECT owner, constraint_name, constraint_type, table_name
FROM dba_constraints
WHERE owner=’&OWNER’ AND table_name=’&SEGMENT_NAME’ AND constraint_type=’P’;
如果有主鍵,再確定是否有訪問該主鍵的外鍵存在:
SELECT owner, constraint_name, constraint_type, table_name
FROM dba_constraints
WHERE r_owner=’&OWNER’ AND r_constraint_name=’&CONSTRAINT_NAME’;
針對普通表或分區的恢復,有如下兩種策略:第一種是通過重建表的方式搶救數據。第二種則是通過DBMS_REPAIR包將壞塊進行標識,將壞塊進行隔離,不影響壞塊之外數據的訪問。後面還將詳細敘述該過程。
- TABLE PARTITION
若壞塊位於分區表(TABLE PARTITION),則查詢是那些Partition受到影響:
SELECT partition_name
FROM dba_extents
WHERE file_id = &AFN AND &BL BETWEEN block_id AND block_id + blocks – 1;
其它操作則同上述普通表的處理,例如查詢該表的索引、主鍵、外鍵信息等。
針對分區表的恢復,除上述重建等策略之外,若壞塊都位於某一個分區,還可以通過如下的分區交換(exchange)方式,將該壞塊數據交換到某個臨時表:
Alter table <table_name> exchange partition <partition_name> with table <Temp_table_name>;
這樣,DBA可以專注在這個臨時表上進行壞塊數據的處理,而該表的其它分區可以正常提供服務。
- INDEX
若壞塊所在的數據對象爲SYS用戶下的數據字典表上的索引,建議聯繫Oracle技術支持部門。
若壞塊位於普通表或分區上的索引,則查詢該索引所在表:
SELECT table_owner, table_name
FROM dba_indexes
WHERE owner=’&OWNER’ AND index_name=’&SEGMENT_NAME’;
並查詢該索引上是否定義了Constraint:
SELECT owner, constraint_name, constraint_type, table_name
FROM dba_constraints
WHERE owner=’&TABLE_OWNER’
AND constraint_name=’&INDEX_NAME’;
若該索引爲主鍵,則查詢是否有訪問該主鍵的外鍵存在:(本文永久地址:https://www.askmac.cn/?p=16580)
SELECT owner, constraint_name, constraint_type, table_name
FROM dba_constraints
WHERE r_owner=’&TABLE_OWNER’ AND r_constraint_name=’&INDEX_NAME’;
針對索引的恢復,最好的辦法就是重建(rebuild)索引,後面還將詳細敘述該過程。
- INDEX PARTITION
若壞塊位於分區索引,則查詢哪些分區受到影響:
SELECT partition_name
FROM dba_extents
WHERE file_id = &AFN AND &BL BETWEEN block_id AND block_id + blocks – 1;
其它操作則同上述普通索引的處理,例如查詢該索引所在表、主鍵、外鍵信息等。
同樣地,針對分區索引的恢復,最好的辦法就是重建(rebuild)索引,例如:
ALTER INDEX <Index_Name> REBUILD PARTITION <Partiton_Name>;
- TEMPORARY
若壞塊位於臨時表空間(TEMPORARY),則壞塊並沒有影響到永久數據對象,例如表、索引等。此時,應查詢出使用該臨時表空間的所有用戶:
SELECT username FROM dba_users
WHERE temporary_tablespace=’&TABLESPACE_NAME’;
針對臨時表空間的恢復,最好的辦法就是創建一個新的臨時表空間,並將受到影響的用戶的臨時表空間設置爲新的臨時表空間。例如:
CREATE TEMPORARY TABLESPACE temp2
TEMPFILE ‘/u01/oradata/temp02.dbf’ SIZE 500M
EXTENT MANAGEMENT LOCAL UNIFORM SIZE 10M;
Alter user <user_name> temporary tablespace temp2;
其它數據類型,例如ROLLBACK、CACHE、CLUSTER、LOBINDEX、LOBSEGMENT、、TYPE2 UNDO、其它數據對象等,就聯繫Oracle技術支持部門,或請見《Handling Oracle Block Corruptions in Oracle7/8/8i/9i/10g/11g [ID 28814.1]》
選擇合適的方法進行數據恢復和搶救
接下來的第四個步驟:我們終於要進行實際的數據恢復和搶救了。但這一步驟太複雜了,我們不得不另闢下面一節展開詳細描述。
壞塊處理八卦圖
所謂“選擇合適的方法進行數據恢復和搶救”,就是根據壞塊所處數據對象的不同類型,例如:CACHE、CLUSTER、INDEX PARTITION、INDEX、LOBINDE、LOBSEGMENT、ROLLBACK、TABLE PARTITION、TABLE、TEMPORARY、TYPE2 UNDO、其它數據對象等,合理制定策略和具體方法進行數據恢復和搶救。
以下就是本人根據Oracle若干篇壞塊處理文檔總結的數據壞塊處理八卦圖:
下面將詳細描述上述八卦圖:
- 首先判斷壞塊影響的數據庫對象是否是已經不使用的數據對象了,如果是,則啥也不用做了。
- 其次,判斷壞塊影響的數據庫對象是否處於臨時表空間,如果是,則參照上述內容:創建一個新的臨時表空間,並將受到影響的用戶的臨時表空間設置爲新的臨時表空間。
- 第三,如果壞塊影響的數據庫對象是索引,則進一步判斷索引所在的表是否也有壞塊。如果有,則先解決表的壞塊問題。如果沒有,則可以通過索引重建方式進行恢復。
若該索引有外鍵存在,則需要按如下步驟進行:
– For each foreign key
ALTER TABLE <child_table> DISABLE CONSTRAINT <fk_constraint>;
– Rebuild the primary key using
ALTER TABLE <table> DISABLE CONSTRAINT <pk_constraint>;
DROP INDEX <index_name>;
CREATE INDEX <index_name> .. with appropriate storage clause
ALTER TABLE <table> ENABLE CONSTRAINT <pk_constraint>;
– Enable the foreign key constraints
ALTER TABLE <child_table> ENABLE CONSTRAINT <fk_constraint>;
如果是分區索引,則重建索引語句如下:
ALTER INDEX … REBUILD PARTITION …;
需要注意的幾點是:
(1)儘量不要使用“ALTER INDEX .. REBUILD”語句去重建非分區索引,因爲該語句可能通過已經含有壞塊的舊索引數據進行重建,而“ALTER INDEX … REBUILD ONLINE”和“ALTER INDEX … REBUILD PARTITION …”則不會通過已經含有壞塊的舊索引數據進行重建,因此應以後兩種語句方式進行索引重建。
(2)假設有壞塊的索引字段是另外一個複合索引字段的子集,則Oracle可能利用該複合索引的數據進行重建。若該複合索引也有壞塊,那就太不幸了。此時,最好將這兩個索引都刪除掉,並重建。
(3)在重新創建索引時,一定要正確設置相關存儲屬性,例如將新索引創建在確保沒有硬件故障的表空間中。
- 第四,此時可考慮數據庫的完全恢復了。但應該滿足如下條件:
- 數據庫處於歸檔狀態
- 備份數據是完整的
建議通過dbv程序對備份數據檢查其完整性。如果最新備份數據也含有壞塊數據,則需要查找更舊的備份數據。
- 歸檔日誌必須是完整的
從備份數據到當前時間的歸檔日誌必須是完整的。
- 聯機日誌必須是完整的
- 沒有對實施了nologging操作的數據對象進行recover操作。
例如,若壞塊只出現在少量數據塊上,則建議進行數據塊級恢復。以下是數據塊級恢復的相關命令:
blockrecover datafile 8 block 13;
Select * from v$database_block_corruption
blockrecover corruption list;
請注意:數據塊級恢復只能做到完全恢復,而不能做到不完全恢復。
若壞塊只出現在少數幾個數據文件上,則建議進行數據文件級恢復。以下是數據文件級恢復的步驟和相關命令:
— 將含壞塊的數據文件設置爲OFFLINE狀態
ALTER DATABASE DATAFILE ‘name_of_file’ OFFLINE;
— 將該文件複製到安全位置,以防備份數據也包含了壞塊
cp < name_of_file > <安全位置>
— 從最新的備份數據中restore該文件至安全位置
命令略
— 通過DBVERIFY檢查該文件是否包含壞塊
命令略
— 假設該文件不包含壞塊,則對該文件目錄進行RENAME操作:
ALTER DATABASE RENAME FILE ‘old_name’ TO ‘new_name’;
— 對該文件進行recover操作
RECOVER DATAFILE ‘name_of_file’;
— 將該數據文件恢復爲ONLINE狀態
ALTER DATABASE DATAFILE ‘name_of_file’ ONLINE;
若壞塊出現在多個數據文件上,則可以考慮進行數據庫級恢復。以下是數據庫級恢復的步驟和相關命令:
— 關閉數據庫
Shutdown (Immediate or Abort)
— 將所有文件複製到安全位置,以防備份數據也包含了壞塊
cp < name_of_file > <安全位置>
— 從最新的備份數據中restore所有文件至安全位置,但不要restore控制文件和聯機日誌文件
命令略
— 通過DBVERIFY檢查所有文件是否包含壞塊
命令略
— 將數據庫啓動到mount狀態
Startup MOUNT
— 假設所有文件不包含壞塊,則對被改動位置的文件進行RENAME操作:
ALTER DATABASE RENAME FILE ‘old_name’ TO ‘new_name’;
— 確保所有文件處於ONLINE狀態:
ALTER DATABASE DATAFILE ‘name_of_file’ ONLINE;
— 對數據庫進行recover操作
RECOVER DATABASE
— 打開數據庫
ALTER DATABASE OPEN;
數據庫完全恢復之後,建議對受影響的數據對象進行完整性檢查,例如:
ANALYZE <table_name> VALIDATE STRUCTURE CASCADE;
確認是否有數據和索引不匹配的情況存在。進一步,建議在應用級檢查數據的邏輯完整性。
- 第五,如果上述完全恢復仍然不能恢復壞塊數據,而且被損壞的表爲關鍵業務數據,則此時需要考慮儘可能先確保這些表的正常對外訪問,並且從這些表中搶救儘可能多的數據。
此時,可供選擇的辦法包括:
- 通過DBMS_REPAIR包或設置10231事件,搶救壞塊之外數據。
- 通過ROWID掃描方法,搶救壞塊之外數據。
由於這兩項技術較爲複雜,我們將在下面專闢章節講述。
DBMS_REPAIR包或設置10231事件
概述
通過DBMS_REPAIR包或設置10231事件,搶救壞塊之外數據的方法,實際上是針對Oracle不同版本而言(本文永久地址:https://www.askmac.cn/?p=16580):
- Oracle 8i之上版本
通過DBMS_REPAIR.SKIP_CORRUPT_BLOCKS過程,對錶設置SKIP_CORRPUPT標誌,達到繞過壞塊讀取正常數據的目的。
- Oracle 7到Oracle 8.1版本
通過設置10231事件,達到繞過壞塊讀取正常數據的目的。該技術在Oracle 7.2之前,一般只針對邏輯壞塊(Soft Corrupt),而無法針對因介質損壞而造成的物理壞塊(Physical Corrupt)。在Oracle 7.2之後,雖然也增強了對物理壞塊的至此和,但仍然不能保證能繞過所有類型的壞塊。
對廣大客戶和現有Oracle版本而言,當然主要將使用DBMS_REPAIR.SKIP_CORRUPT_BLOCKS技術,但在本書中我們仍然將介紹設置10231事件技術。
欲使用這兩類技術,必須滿足如下條件:
- 壞塊所在的表必須是普通表,而不能是系統數據字典表。
- 最好在Oracle技術支持部門建議和指導下,採用這兩類技術。
- 已經確定瞭如何重建或搶救數據的辦法。例如,Export,或者“create tables as select…”。
- 已經計劃好了搶救數據的停機時間窗口。另外,如果有可能,最好能有一份壞塊所在表的複製數據,這樣可以專注在這份複製數據上進行數據搶救工作。
- 整個數據庫有備份數據
- 已經準備好了重建該數據壞塊表的SQL腳本,包括索引、限制、觸發器等,以及相關的物理存儲屬性參數。
實施過程
- SKIP_CORRPUPT標誌的設置
通過如下語句,可對壞塊所在表進行SKIP_CORRPUPT標誌的設置:
SQL> connect /as sysdba;
SQL> execute DBMS_REPAIR.SKIP_CORRUPT_BLOCKS(<用戶名>,<表名>);
這樣,可通過“CREATE TABLE AS SELECT”、“or ALTER TABLE <> MOVE”、Export等技術,將非壞塊數據進行搶救,例如:
SQL> CREATE TABLE salvage_emp AS SELECT * FROM corrupt_emp;
設置SKIP_CORRPUPT標誌之後,若進行了跳過壞塊的操作,Oracle將在相關trace文件中進行記錄,例如:
table scan: segment: file# 6 block# 11
skipping corrupt block file# 6 block# 12
欲清除SKIP_CORRPUPT標誌,則執行如下語句:
SQL> connect /as sysdba;
SQL> execute DBMS_REPAIR.SKIP_CORRUPT_BLOCKS(<用戶名>,<表名>, flags=>dbms_repair.noskip_flag);
值得注意的是:DBMS_REPAIR.SKIP_CORRUPT_BLOCKS也可針對索引進行設置,但Oracle進行index range scan操作時,只對非根節點的葉節點進行壞塊跳躍,而對枝節點和根結點並不進行壞塊跳躍。
- 10231事件的設置
10231事件的設置可以在session或instance級分別進行。如果採用“CREATE TABLE AS SELECT”或“or ALTER TABLE <> MOVE”進行數據搶救,則在session設置即可。如果採用Export進行數據搶救,則應在instance級進行設置。
例如,以下語句在session級設置10231事件:
SQL> ALTER SESSION SET EVENTS ‘10231 TRACE NAME CONTEXT FOREVER, LEVEL 10’;
此時,可通過“CREATE TABLE AS SELECT”、“or ALTER TABLE <> MOVE”、Export等技術,將非壞塊數據進行搶救,例如:
SQL> CREATE TABLE salvage_emp AS SELECT * FROM corrupt_emp;
欲在instance級進行設置,則在初始化文件init.ora或spfile.ora文件中進行如下設置,並重啓數據庫:
event=”10231 trace name context forever, level 10″
搶救數據之後的處理
一旦將數據搶救完畢,例如重建新表,或者將數據export出來,則應進行如下事後處理:
- 對被搶救數據進行備份
- 保存重建表、索引的SQL腳本
- 將診斷和處理過程中,Oracle技術支持人員需要的相關診斷信息加以保存
- 刪除10231事件或將清除SKIP_CORRPUPT標誌
- 對原來有問題的表進行RENAME或DROP操作。如果空間富裕,最好是RENAME操作。
- 通過import操作等重建原表。
- 重建相關索引、觸發器等其它對象
ROWID掃描方法
上述通過DBMS_REPAIR包或設置10231事件,搶救壞塊之外數據的方法,相比即將介紹的ROWID掃描方法,更爲簡潔。但上述方法只適合於出現ORA-1578的情況,而且根據本人的經驗,該方法經常不能有效進行數據搶救。因此,下面我們將詳細介紹ROWID掃描方法。
ROWID簡介
爲有效使用ROWID掃描方法,我們先需要介紹一下ROWID的格式。ROWID表示每條記錄在數據庫中的的物理地址,在Oracle 8i之後,ROWID被表示爲18位的數字字符串
‘OOOOOOFFFBBBBBBSSS’
其中:
- OOOOOO:以Base 64 Encoding 編碼格式表示的數據對象序號
- FFF:以Base 64 Encoding 編碼格式表示的相對文件號
- BBBBBB:以Base 64 Encoding 編碼格式表示的數據塊號
- SSS:以Base 64 Encoding 編碼格式表示的slot或row號。
通過如下函數,可創建一條記錄的ROWID:
function ROWID_CREATE(rowid_type IN number,
object_number IN number,
relative_fno IN number,
block_number IN number,
row_number IN number)
return ROWID;
— rowid_type – type (restricted=0/extended=1)
— object_number – data object number
— relative_fno – relative file number
— block_number – block number in this file
— row_number – row number in this block
ROWID掃描方法原理
以下就是該技術流程圖和示意圖:
即在定位壞塊之後,通過DBMS_ROWID包去生成壞塊所處的最小ROWID(LOW_RID),以及最大ROWID(HIGH_RID),例如:
— 最小ROWID
SELECT dbms_rowid.rowid_create(1,<DATA_OBJECT_ID>,<RFN>,<BL>,0) LOW_RID from DUAL;
— 最大ROWID
SELECT dbms_rowid.rowid_create(1,<DATA_OBJECT_ID>,<RFN>,<BL>+1,0) HI_RID from DUAL;
針對普通表,通過如下命令搶救數據:
CREATE TABLE salvage_table AS SELECT /*+ ROWID(A) */ * FROM <owner.tablename> A WHERE rowid < ‘<low_rid>’ ;
INSERT INTO salvage_table SELECT /*+ ROWID(A) */ * FROM <owner.tablename> A WHERE rowid >= ‘<high_rid>’ ;
針對分區表,通過如下命令搶救數據:
CREATE TABLE salvage_table AS
SELECT /*+ ROWID(A) */ *
FROM <owner.tablename> PARTITION (<partition_name>) A
WHERE rowid < ‘<lo_rid>’
;
INSERT INTO salvage_table
SELECT /*+ ROWID(A) */ *
FROM <owner.tablename> PARTITION (<partition_name>) A
WHERE rowid >= ‘<hi_rid>’
但是,如果壞塊處於表段頭(Segment Header),ROWID掃描法則無用武之地了。通過如下語句,可知道壞塊是否處於表段頭:
select file_id,block_id,blocks,extent_id
from dba_extents
where owner='<owner>’
and segment_name='<table_name>’
and segment_type=’TABLE’
order by extent_id;
FILE_ID BLOCK_ID BLOCKS EXTENT_ID
——— ——— ——— ———
8 94854 20780 0 <- EXTENT_ID ZERO is segment header
即上述語句中,如果EXTEND_ID爲0,則表示是表段頭。
如何從壞塊中搶救數據?
首先,Oracle公司認爲,既然數據已經壞了,壞塊數據被完全搶救出來的可能性就微乎其微了。即便這樣,可供選擇的辦法有:
通過索引從壞塊中搶救數據
通過ROWID掃描法,可以通過索引從壞塊中搶救被索引字段的數據。以下就是詳細過程:
- 如果需要搶救的字段是非空值(Not Null)字段,則使用Fast Full Scan訪問方式:
SELECT /*+ INDEX_FFS(X <index_name>) */
<index_column1>, <index_column2> …
FROM <tablename> X
WHERE rowid >= ‘<low_rid>’
AND rowid < ‘<hi_rid>’ ;
其中,<low_rid>、<hi_rid>通過上述dbms_rowid.rowid_create語句產生。
- 如果需要搶救的字段是允許空值(Null)字段,則不能使用Fast Full Scan,而必須使用Range Scan訪問方式。爲此,必須設置索引前綴字段的最小值條件,才能確保使用Range Scan訪問方式:
SELECT /*+ INDEX(X <index_name>) */
<index_column1>, <index_column2> …
FROM <tablename> X
WHERE rowid >= ‘<low_rid>’
AND rowid < ‘<hi_rid>’
AND <index_column1> >= <min_col1_value>;
這樣,如果壞塊所在表的索引越多,從索引中搶救數據也可能越多。通過查詢dba_ind_columns視圖可查詢表的索引信息。
通過LogMiner技術
通過LogMiner技術,也可能對壞塊數據進行一定的搶救。即從日誌文件中找到最初加載到壞塊的Insert或Update語句,並從這些語句中搶救出相關數據。
尋求Oracle Support支持
針對表的壞塊數據,通過尋求Oracle Support支持,可以通過相關內部工具,對壞塊數據進行搶救。欲通過此方法進行進行搶救,請在MOS網站中創建SR。
壞塊搶救的最後招數
- 從容災數據庫進行搶救
如果通過Data Guard配置了容災數據庫,由於Data Guard具有防止壞塊傳播功能,因此,可考慮在容災數據庫對相關數據對象進行搶救。
- 從頭恢復的一種場景
假設某個數據文件出現壞塊,而且數據庫沒有物理備份,但保留了該數據文件創建之後的所有歸檔日誌,則可以通過如下方式進行恢復:
— 重新創建該數據文件
ALTER DATABASE CREATE DATAFILE ‘….’ [as ‘…’] ;
— 對該數據文件進行恢復操作
RECOVER DATAFILE ‘….’
— 將該數據文件進行恢復爲Online狀態
ALTER DATABASE DATAFILE ‘….’ ONLINE;
- 不完全恢復
無論何種數據類型的壞塊,都可以通過不完全恢復技術,將整個數據庫,或者將壞塊所在的表空間恢復到壞塊發生之前的某個時刻。但這種技術運用的前提是確定了壞塊出現的準確時間,而且將導致整個數據庫,或某些表空間數據的大範圍回退。
- 冷備份恢復
假設數據庫爲非歸檔模式,並且有完好的冷備份數據,則可以進行冷備份恢復,但只能恢復到冷備份的時間點。也可通過冷備份數據克隆一個數據庫,並從此克隆數據庫中進行數據搶救。
- 邏輯恢復
最後一個招數之一就是通過邏輯備份數據(Export,Data Pump)進行恢復了。邏輯恢復只能恢復到備份時間點,不能實現完全恢復。當然,也可通過邏輯備份數據克隆一個數據庫,並從此克隆數據庫中進行數據搶救。
本章參考資料及進一步讀物
本章參考資料及進一步讀物:
序號 | 資料類別 | 資料名稱 | 資料概述 |
1. | My Oracle Support | 《Master Note for Handling Oracle Database Corruption Issues (Doc ID 1088018.1)》 | 這就是有關數據庫壞塊問題的資料入口。壞塊的解釋;壞塊的種類;表、索引、IOT、LOB等各類數據對象壞塊的處理;各類壞塊診斷工具等,應有盡有。 |
2. | My Oracle Support | 《Handling Oracle Block Corruptions in Oracle7/8/8i/9i/10g/11g (Doc ID 28814.1)》 | 這就是本章主要參考的文檔。 |
3. | My Oracle Support | 《Extracting Data from a Corrupt Table using DBMS_REPAIR or Event 10231 (Doc ID 33405.1)》 | 這是從壞塊表中抽取數據的最簡單辦法。儘管不一定奏效,但值得一試。 |
4. | My Oracle Support | 《Extracting Data from a Corrupt Table using ROWID Range Scans in Oracle8 and higher (Doc ID 61685.1)》 | 雖然這篇文章介紹的從壞塊表中抽取數據方法有點複雜,但效果還是不錯的。 |
5. | My Oracle Support | 《Best Practices for Avoiding and Detecting Corruption (Doc ID 428570.1)》 | 對付壞塊最有效的辦法就是防患於未然。看看這篇防範壞塊的最佳實踐經驗文章吧:設置DB_BLOCK_CHECKING、DB_BLOCK_CHECKSUM參數;RMAN備份加強邏輯壞塊檢查;定期運行dbv;定期對主要表進行analyze操作… … |
6. | My Oracle Support | 《How to identify all the Corrupted Objects in the Database with RMAN (Doc ID 472231.1)》 | RMAN可是允許壞塊存在的。該文檔描述瞭如何查找RMAN備份數據中的壞塊,以及所對應的數據段。 |
7. | My Oracle Support | 《How to identify the corrupt Object reported by ORA-1578 / RMAN / DBVERIFY (Doc ID 819533.1)》 | 該文章介紹根據ORA-1578錯誤信息,或者根據RMAN和DBVERIFY的壞塊信息,查詢對應數據對象的辦法。 |
運行維護篇
本人曾在10餘年前從數據庫應用開發人員轉爲一個網站的專職DBA,當我把數據庫安裝好,把備份恢復做好,然後就開始發木了:DBA還應該做什麼工作?可能現在很多專職DBA還有類似困惑。Oracle 11g聯機文檔《Oracle® Database Administrator’s Guide》第一章描述了DBA的11大任務:評估數據庫服務器硬件、安裝數據庫軟件、數據庫規劃、性能監控和優化、備份和恢復數據庫、下載和安裝補丁… ….
本篇就將從運行維護角度介紹幾項DBA應該做的典型工作:數據庫健康檢查、防止人爲錯誤的FLASHBACK技術運用、版本和補丁管理、數據庫空間和碎片管理、數據庫安全性評估和加固… …
希望DBA們看過之後,會發出這樣的感慨:哦,原來DBA有那麼多事情可做哦!
轉載自: