目錄
1、物理壞塊與邏輯壞塊
1.1、物理壞塊
1.2、邏輯壞塊
2、兩個壞塊相關的參數
2.1、db_block_checksum
2.2、db_block_checking
3、檢測壞塊?
3.1、告警日志
3.2、RMAN
3.3、ANALYZE
3.4、數據字典
3.5、DBVERIFY
4、修復壞塊
4.1、RMAN修復
4.2、DBMS_REPAIR修復
4.3、BBED
5、寫在最后
1、物理壞塊與邏輯壞塊
????????將數據庫比喻成一本書,每一頁看作是一個數據塊。當我們看不懂書中某頁的內容可能有3種原因:該頁缺損、該頁內容邏輯有錯誤、智商不夠。我們的智商顯然不是問題,這點首先排除。
????????所以書頁缺損、書頁上的內容邏輯錯誤是最可能的兩點原因。其中頁書缺損就好比數據庫中的物理壞塊,直接粗暴的破壞了這一頁;書頁上的內容邏輯錯誤就像是數據庫中的邏輯壞塊,寫的內容驢頭不對馬嘴。
????????如果現在寫的這篇文章讓您讀著不通順或者讀不懂,那這篇文章也屬于是個邏輯壞塊。
1.1、物理壞塊
????????存儲介質導致的數據塊損壞,常見的場景是磁盤損壞、網絡故障導致數據塊在傳輸時發生損壞,突然斷電導致數據塊的元數據損壞。
1.1.1、物理壞塊的常見錯誤表現
1.1.1.1、Fractured Block(斷塊)
????????斷塊是指數據塊不完整,塊頭信息與塊尾信息不匹配。
Corrupt block relative dba: 0x0380e573 (file 14, block 58739)
Fractured block found during buffer read
Data in bad block -
type: 6 format: 2 rdba: 0x0380e573
last change scn: 0x0288.8e5a2f78 seq: 0x1 flg: 0x04
consistency value in tail: 0x00780601
check value in block header: 0x8739, computed block checksum: 0x2f00
spare1: 0x0, spare2: 0x0, spare3: 0x0
***
Reread of rdba: 0x0380e573 (file 14, block 58739) found same corrupted data
1.1.1.2、Bad checksum
? ? ? ? 這個錯誤是指checksum不一致。
????????checksum是數據塊的校驗和,它是Oracle將數據塊中的內容通某種算法進行計算生成的一個唯一的校驗值。checksum存儲在數據塊的頭部;當讀取數據塊時,Oracle會重新計算checksum,與存儲在數據塊頭部的checksum進行比較,如果不一致,就判斷數據塊發生了損壞。
Corrupt block relative dba: 0x0380a58f (file 14, block 42383)
Bad check value found during buffer read
Data in bad block -
type: 6 format: 2 rdba: 0x0380a58f
last change scn: 0x0288.7784c5ee seq: 0x1 flg: 0x06
consistency value in tail: 0xc5ee0601
check value in block header: 0x68a7, computed block checksum: 0x2f00
spare1: 0x0, spare2: 0x0, spare3: 0x0
***
Reread of rdba: 0x0380a58f (file 14, block 42383) found same corrupted data
1.1.1.3、Block Misplaced
????????Oracle檢測到正在讀取的數據塊的內容屬于另一個塊,但是checksum有效。
?
Corrupt block relative dba: 0x0d805a89 (file 54, block 23177)
Bad header found during buffer read
Data in bad block -
type: 6 format: 2 rdba: 0x0d805b08 ----> Block is different than expected 0x0d805a89
last change scn: 0x0692.86dc08e3 seq: 0x1 flg: 0x04
consistency value in tail: 0x08e30601
check value in block header: 0x2a6e, computed block checksum: 0x0
spare1: 0x0, spare2: 0x0, spare3: 0x0
1.1.1.4、Zeroed out blocks
????????如果Oracle數據庫發生了Zeroed out blocks,那么基本可以判斷是操作系統或者存儲系統發生了故障。
????????根據Oracle官方文檔描述,在設計的時候,Oracle就被確定不會寫全零塊,目的是為了識別底層操作系統或者存儲中的故障。
Corrupt block relative dba: 0x014000a0 (file 5, block 160)
Completely zero block found during multiblock buffer read
1.2、邏輯壞塊
????????邏輯壞塊是指數據塊的內容或者結構不符合Oracle內部規范,但是在物理存儲介質方面沒有問題。雖然發生了邏輯壞塊,但其checksum很可能并無問題,只是數據塊中的內容發生了損壞。
????????邏輯壞塊的詳細損壞描述通常不會輸出到告警日志中,這點是和物理壞塊不同的。
1.2.1、邏輯壞塊常見錯誤表現:
1.2.1.1、ORA-600 [4512]
ORA-600 [4512],被不存在的事務鎖定了行。
1.2.1.2、不符合自然邏輯
日期數據類型字段,存入了2025-03-32 25:61:61這種不符合自然邏輯的數據。
1.2.1.3、數據字典邏輯損壞
Oracle數據字典字典表發生了字典數據與實際不一致的情況;例如:dba_tables顯示某表存在,但是實際的數據文件沒有這個表段。
執行DDL操作時,觸發ORA-00600 [kdsgrp1],或者元數據查詢失敗。
1.2.1.4、索引失效
索引指向了無效的ROWID,比如說數據行已經被刪除了,但是索引沒有更新。
索引掃描時,返回ORA-01410(無效的ROWID),或者查詢結果不匹配。
1.2.1.5、數據塊頭部元數據損壞
數據塊的頭部信息,例如塊的類型、SCN、事務槽遭到損壞。例如數據塊的類型本來是表段塊,但是實際存儲的卻是索引,這種錯誤就屬于是Oracle軟件的bug了。
訪問到這種邏輯壞塊時,觸發ORA-00600 [kddummy_blkchk]內部錯誤。
2、兩個壞塊相關的參數
2.1、db_block_checksum
????????這個參數是用來控制checksum的生成.
????????checksum是數據塊的校驗和,它是Oracle將數據塊中的內容通某種算法進行計算生成的一個唯一的校驗值。checksum存儲在數據塊的頭部;當讀取數據塊時,Oracle會重新計算checksum,與存儲在數據塊頭部的checksum進行比較,如果不一致,就判斷數據塊發生了損壞。
????????可以通過show parameter來查看當前數據庫對于此參數的配置情況。
SQL> show parameter db_block_checksumNAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_block_checksum string TYPICAL
????????db_block_checksum參數有3種選項:FULL、TYPICAL、OFF,下面對這三種選項進行簡單說明。
參數選項 | 簡單說明 |
FULL | 對所有數據塊,包括數據文件、控制文件、日志文件生成校驗和。 |
TYPICAL | 默認值,對數據文件、控制文件生成校驗和,不會作用于臨時表空間。 |
OFF | 不生成checksum,這個是不推薦的。 |
? ? ? ? 可以修改db_block_checksum參數,但因為是靜態參數,修改完參數需要重啟數據庫。
ALTER SYSTEM SET db_block_checksum = FULL SCOPE=SPFILE;
2.2、db_block_checking
????????這個參數是對數據塊的邏輯一致性檢查,當數據塊在被讀取或者修改時,自動檢測數據塊內部結構的邏輯錯誤,從而防止因為邏輯損壞導致數據丟失。
????????db_block_checking參數有4種選項:HIGH、MEDIUM、LOW、OFF,下面對這4種選項進行簡單的說明。
參數選項 | 簡單說明 |
HIGH | 執行完整的邏輯檢查,包括索引塊、事務槽,覆蓋數據庫所有的修改操作。 |
MEDIUM | 對所有表空間的數據塊執行基本檢查,例如塊頭等。 |
LOW | 只對數據庫的系統表空間執行數據塊檢查。 |
OFF | 不對任何數據塊執行檢查,這是個默認值。 |
????????可以修改db_block_checking參數,但因為是靜態參數,修改完參數需要重啟數據庫。
ALTER SYSTEM SET db_block_checking=HIGH SCOPE=SPFILE;
? ? ? ?db_block_checksum與db_block_checking參數的不同點在于db_block_checksum是用來檢測物理完整性的,主要是服務于物理壞塊問題處理;db_block_checking是用來檢測邏輯一致性的,主要是服務于邏輯壞塊問題處理。
????????db_block_checksum與db_block_checking參數的相同點是當啟用參數時,啟用的級別的越高,對性能的影響就會越大。所以在使用這兩個參數時,需要在數據庫性能和數據安全這兩方面做好平衡。
3、檢測壞塊?
????????Oracle數據庫提供了幾種手段可以對壞塊進行監測,下面對這些手段進行逐一介紹。這些手段側重點和使用場景都有不同,有的也可以組合使用,效率效果更佳。
3.1、告警日志
????????告警日志是DBA最常看的日志,每次巡檢如果繞開它,個人覺得都不算一次標準的巡檢。Oracle在檢測到數據壞塊時,會在告警日志中記錄錯誤信息。通過檢查告警日志,可以及時發現數據壞塊錯誤。
????????確定告警日志位置,以下兩個方法都可以。
SQL> show parameter dumpNAME TYPE VALUE
------------------------------------ ----------- ------------------------------
background_core_dump string partial
background_dump_dest string /oracle/app/diag/rdbms/htbase/htbase1/trace
core_dump_dest string /oracle/app/diag/rdbms/htbase/htbase1/cdump
max_dump_file_size string unlimited
shadow_core_dump string PARTIAL
user_dump_dest string /oracle/app/diag/rdbms/htbase/htbase1/traceSQL> SELECT value FROM v$diag_info WHERE name = 'Diag Trace';VALUE
--------------------------------------------------------------------------------
/oracle/app/diag/rdbms/htbase/htbase1/trace
????????檢索壞塊錯誤記錄
? ? ? ? 在告警日志中搜索“ORA-01578”,或者搜素“Corrupt block“關鍵字。這個大家可以看上文物理壞塊介紹那里就知道為什么檢索兩個了。
3.2、RMAN
????????RMAN也是官方推薦壞塊檢測工具,支持對數據文件、控制文件、歸檔日志文件等進行完整性檢查,個人覺得RMAN比較適合作為一種定期健康檢查的手段。RMAN有下面幾種檢查方式。
3.2.1、檢查整個數據庫
????????可以輸出所有數據文件、控制文件、日志文件的壞塊信息。
RMAN> VALIDATE DATABASE;
3.2.2、檢查指定的數據文件
????????可以輸出數據文件內每個塊的物理和邏輯一致性。
RMAN> VALIDATE DATAFILE 28; -- 文件號為28的數據文件
3.2.3、邏輯一致性檢查
????????不僅檢查數據塊的物理完整性,還會檢查數據塊的邏輯結構。
RMAN> VALIDATE CHECK LOGICAL DATABASE;
3.2.4、檢查備份集
????????這個命令,無論是新手還是老手都不陌生,備份常用命令,檢查備份文件是否損壞,確保恢復的時候是可用的。
RMAN> VALIDATE BACKUPSET <備份集編號>;
3.3、ANALYZE
ANALYZE命令可以檢查表、索引的邏輯一致性。
3.3.1、檢查表結構
????????如果存在壞塊,會拋出ORA-01498錯誤。此時可能是數據塊頭部的元數據信息出錯、索引對應數據塊的關聯關系有誤(這些都在上文的邏輯壞塊部分介紹過);如果是物理壞塊,還會伴隨著ORA-01578。
ANALYZE TABLE hr.zqd VALIDATE STRUCTURE;
3.3.2、檢查索引的一致性
????????如果索引與表數據不匹配,會拋出ORA-01499錯誤。這可能是索引和表數據沒同步,例如刪除大量數據后,沒有重建索引;事務異常中斷導致索引條目沒有正常更新等等。如果是物理損壞的話,會伴隨著ORA-01578錯誤。
? ? ? ? 此時比較高效的修復操作是重建索引,但不一定能解決所有場景下的此類問題。
ANALYZE INDEX HR.zqd_idx VALIDATE STRUCTURE;
3.4、數據字典
3.4.1、V$DATABASE_BLOCK_CORRUPTION
SELECT * FROM V$DATABASE_BLOCK_CORRUPTION;
????????這個視圖會記錄Oracle數據庫中檢測到壞塊信息。這個視圖的數據寫入觸發機制包含以下幾種途徑。
操作 | 具體操作 |
RAMN | 1、使用RMAN進行備份的時候,會驗證數據塊的完整性,若發現壞塊,會寫入該視圖。 2、使用RMAN進行VALIDATE驗證時,若發現壞塊,會寫入該視圖。 3、使用RMAN進行RECOVER恢復時,若發現壞塊,會寫入該視圖。 |
進程檢測 | 1、讀取數據時,checksum不一致,會將壞塊信息寫入該視圖。 2、SELECT語句訪問到壞塊時,會將壞塊信息寫入該視圖。 |
工具檢查 | 1、DBVERIFY作為外部工具,不會直接將壞塊信息寫入視圖,但可以通過日志文件手動確認壞塊后,需要通過數據庫操作才會將壞塊信息寫入視圖。 2、ANALYZE命令若檢測到壞塊可能會將壞塊信息寫入視圖。 |
3.4.2、DBA_EXTENTS
????????通過這個Oracle系統基礎表,可以定位到具體的壞塊所在對象,可能是表、索引、大字段。
SELECT owner, segment_name, segment_type
FROM dba_extents
WHERE file_id = 5 -- 文件號AND 1234 BETWEEN block_id AND block_id + blocks -1; -- 塊號
3.5、DBVERIFY
????????DBVERIFY是Oracle數據庫提供的命令行工具,用于檢查數據文件物理和邏輯一致性,重點檢查數據塊的完整性。
3.5.1、DBVERIFY有幾個特點,主要包含以下幾個方面。
3.5.1.1、只讀方式打開
? ? ? ? 它以只讀的方式打開數據文件,不會修改數據文件,確保了數據文件的安全性。
3.5.1.2、在線檢查
????????它支持在線檢查數據文件,不需要關閉數據庫。當然離線時對數據文件的檢查速度更快。
3.5.1.3、支持對ASM文件的檢查
? ? ? ? 在數據庫是正常打開的情況下,通過userid參數指定用戶權限,即可檢查ASM文件。
dbv file=+DG1/orcl/datafile/system01.dbf userid=system/sys
3.5.2、DBVERIFY是有局限性的,主要包含以下幾個方面:
3.5.2.1、只檢查數據文件
????????它只能檢查數據文件;不能檢查控制文件和日志文件。
3.5.2.2、不檢查邏輯關聯性
? ? ? ? 只會檢查數據塊內部的結構,不會檢查索引與表數據的匹配關系。
3.5.2.3、文件大小限制
????????在某些平臺下,不能檢查超過2G的數據文件(但這一點我自己沒有實際驗證過)。
3.5.3、常用與幫助
????????DBVERIFY工具的命令參數有很多,但常用的就是FILE、BLOCKSIZE、LOGFILE等。如果有特殊需要可以查看幫助:dbv help=y
dbv FILE=/oradata/users01.dbf BLOCKSIZE=8192 LOGFILE=/tmp/dbv.log
4、修復壞塊
4.1、RMAN修復
? ? ? ? 最理想的是使用RMAN恢復,進而實現修復壞塊的目的。但這是需要當前數據庫有備份,并且備份可用無壞塊的情況下。
4.1.1、使用RMAN進行塊恢復
????????檢測到數據庫的壞塊、壞塊所在數據文件后,可以指定壞塊進行塊恢復。建議是在數據庫壞塊不多的情況下使用塊恢復,這種修復效率很高;如果是恢復整個數據文件,恢復效率會相對很低。
RMAN> VALIDATE BACKUPSET ALL; --檢查所有備份集是否可用,且有無壞塊。RMAN> VALIDATE BACKUPSET <備份集編號>; --指定備份集進行檢查,檢查是否可用,且有無壞塊。
RMAN> VALIDATE DATABASE; --檢測壞塊位置
RMAN> blockrecover?datafile?4?block?1798; --進行塊恢復--也可以一次修復同一數據文件中的多個壞塊
RMAN> blockrecover?datafile?4?block?1798,1799; --進行多個壞塊恢復
VALIDATE DATAFILE 4 BLOCK 1798; --驗證修復結果
4.1.2、使用RMAN進行數據文件恢復
????????如果數據庫中的壞塊很多,或者進行RMAN塊恢復失敗時,可以嘗試使用RMAN進行數據文件恢復。
? ? ? ? 使用RMAN進行數據文件恢復會有幾點事項需要注意:恢復期間,數據庫部分不可用,因為壞塊所在的數據文件需要OFFLINE;需要確保歸檔日志完整以支持完全恢復;如果壞塊所在的數據文件比較大,恢復效率不會很高。
RMAN> VALIDATE BACKUPSET ALL; --檢查所有備份集是否可用,且有無壞塊。RMAN> VALIDATE BACKUPSET <備份集編號>; --指定備份集進行檢查,檢查是否可用,且有無壞塊。
RMAN> VALIDATE DATABASE; --檢測壞塊位置
SQL> ALTER DATABASE DATAFILE 4 OFFLINE; --離線壞塊所在的數據文件
RMAN> RESTORE DATAFILE 4; --從備份恢復數據文件
RMAN> RECOVER DATAFILE 4; --還原壞塊所在的數據文件
SQL> ALTER DATABASE DATAFILE 4 ONLINE; --切換數據文件為ONLINE狀態。
4.1.3、補充
????????上文的備份集檢查是建議每次備份完就執行檢查,定期巡檢也加入這類檢查。如果備份集不可用或者存在壞塊,那么使用RMAN進行壞塊修復的這條路就斷掉了。
4.2、DBMS_REPAIR修復
????????Oracle數據庫提供了DBMS_REPAIR包用于修復壞塊。但這個方法在修復壞塊方面往往是不奏效的。擬人化的講,我個人覺得這個DBMS包有點 “打的過就打,打不過就跑” 的感覺。因為它雖然很多時候修不好壞塊,但是它能讓Oracle的增刪改查操作跳過壞塊;這也就導致了使用DBMS_REPAIR方法非常可能會丟失數據。但是在沒有備份的情況下,這也是發生壞塊時,能拯救部分數據的方法之一。
????????下圖是DBMS_REPAIR的常用方法總結。
? ? ? ? 現在Oracle數據庫中HR.ZQD0318表有個壞塊,接下來詳細描述下DBMS_REPAIR修復/跳過壞塊的步驟。
SQL> SELECT COUNT(*) FROM HR.ZQD0318;COUNT(*)
----------72594
4.2.1、創建REPAIR表
????????REPAIR表用于記錄需要被修復的表。REPAIR表創建完后,是不會立刻有數據的;在檢查完對象的數據塊受損情況后,REPAIR表才會有數據。
BEGIN
DBMS_REPAIR.ADMIN_TABLES(TABLE_NAME => 'REPAIR_TABLE',TABLE_TYPE => DBMS_REPAIR.REPAIR_TABLE,ACTION => DBMS_REPAIR.CREATE_ACTION,TABLESPACE => 'ZQD');
END;
--如果在SQLPLUS中執行PLSQL代碼塊,需要在代碼塊末尾加上“/”
4.2.2、?創建ORPHAN表
????????ORPHAN表是用來存放在表出現壞塊后的孤立(OPRHAN)索引相關信息,也就是指向那些壞塊的索引信息。ORPHAN表創建完后,不會立刻有數據;在使用DUMP_ORPHAN_KEYS過程后,才會有指向壞塊的索引相關信息。
BEGINDBMS_REPAIR.ADMIN_TABLES(TABLE_NAME => 'ORPHAN_KEY_TABLE',TABLE_TYPE => DBMS_REPAIR.ORPHAN_TABLE,ACTION => DBMS_REPAIR.CREATE_ACTION,TABLESPACE => 'ZQD');
END;
--如果在SQLPLUS中執行PLSQL代碼塊,需要在代碼塊末尾加上“/”
4.2.3、檢查對象的數據塊受損情況
????????檢查完后,壞塊信息會寫入REPAIR表。
DECLAREcorruptnum INT;
BEGINcorruptnum := 0;DBMS_REPAIR.CHECK_OBJECT(SCHEMA_NAME => 'HR',OBJECT_NAME => 'ZQD0318',REPAIR_TABLE_NAME => 'REPAIR_TABLE',CORRUPT_COUNT => corruptnum);DBMS_OUTPUT.PUT_LINE('壞塊數量:' || TO_CHAR(corruptnum));
END;
--如果在SQLPLUS中執行PLSQL代碼塊,需要在代碼塊末尾加上“/”
4.2.4、查看壞塊信息
SELECT OBJECT_NAME,BLOCK_ID,CORRUPT_TYPE,MARKED_CORRUPT,REPAIR_DESCRIPTION FROM REPAIR_TABLE;
4.2.5、嘗試修復壞塊
????????如果輸出是0,則說明修復了0個壞塊,修復無效,此時可以選擇跳過壞塊。
DECLARE
fixnum number;
BEGINDBMS_REPAIR.FIX_CORRUPT_BLOCKS(SCHEMA_NAME => 'HR',OBJECT_NAME => 'ZQD0318',FIX_COUNT => fixnum);DBMS_OUTPUT.PUT_LINE('修復壞塊的數量:' || TO_CHAR(fixnum));
END;
--如果在SQLPLUS中執行PLSQL代碼塊,需要在代碼塊末尾加上“/”
4.2.6、?記錄指向壞塊的索引相關信息
????????執行完如下的PLSQL代碼塊后,失效索引相關信息(指向壞塊的索引相關信息)會寫入ORPHAN表。PLSQL代碼塊中的OBJECT_NAME是索引的名字,不是表名;如果有多個索引,需要為每個索引執行DUMP_ORPHAN_KEYS操作。
DECLAREDATAS NUMBER;
BEGINDBMS_REPAIR.DUMP_ORPHAN_KEYS(SCHEMA_NAME => 'HR',OBJECT_NAME => 'IDX_ZQD',OBJECT_TYPE => DBMS_REPAIR.INDEX_OBJECT,REPAIR_TABLE_NAME => 'REPAIR_TABLE',ORPHAN_TABLE_NAME => 'ORPHAN_KEY_TABLE',KEY_COUNT => COUNTS);DBMS_OUTPUT.PUT_LINE('孤立索引數量:' || TO_CHAR(COUNTS));
END;
4.2.7、跳過壞塊
????????執行下面PLSQL代碼塊后,壞塊已被標記跳過。此時增刪改查都不會報錯,但壞塊上的這部分數據也就丟失了。但丟失的數據的ROWID依然可以在ORPHAN表找到。
BEGINDBMS_REPAIR.SKIP_CORRUPT_BLOCKS(SCHEMA_NAME => 'HR',OBJECT_NAME => 'ZQD0318',OBJECT_TYPE => DBMS_REPAIR.TABLE_OBJECT,FLAGS => DBMS_REPAIR.SKIP_FLAG);
END;
--如果在SQLPLUS中執行PLSQL代碼塊,需要在代碼塊末尾加上“/”
4.2.8、重建索引
ALTER INDEX HR.IDX_ZQD REBUILD
4.2.9、驗證結果
SQL> SELECT COUNT(*) FROM HR.ZQD0318;COUNT(*)
----------72542
4.2.10、后續
????????上面的一套組合拳打完,表雖然恢復了訪問、但是也丟失了部分數據。即使丟失的數據可以通過其他方式補回來(例如數據倉庫可以再重新抽取數據),也存在一個問題:壞塊依然存在。
????????此時,將表中的數據全部導出(導出時跳過了壞塊數據),然后重建表;再將導出的數據重新導入到數據庫中;這個過程建議是使用數據泵expdp/impdp操作。操作完畢后,可以徹底消除壞塊。
4.3、BBED
????????BBED:Block Browser and Editor(塊瀏覽器和編輯器)
? ? ? ? BBED直接操作底層數據塊,風險很高,需要非常專業的人士來操作。且操作不可逆,是作為壞塊修復的最后一種手段使用。
5、寫在最后
????????上面簡單介紹了壞塊的3種修復手段;但當真在生產上遇到了壞塊情況,僅憑這篇文章的描述,很難高效解決問題。這篇文章只是基礎了解,想要高效解決壞塊問題,除了基礎以外,需要平時多做模擬演練,定期總結,才能在遇到壞塊時,快速給出最完美的解決方案。