大家好,這里是公眾號 DBA學習之路,分享一些學習數(shù)據(jù)庫路上的知識和經(jīng)驗。

目錄
前言
Oracle 數(shù)據(jù)庫的運行不可避免的會遇到各種各樣的錯誤,就比如數(shù)據(jù)表出現(xiàn)壞塊,此時,你這張表的數(shù)據(jù)就無法訪問了,有什么好的辦法可以恢復呢?
什么,你沒有遇到過???
?? 那就祝你不久的將來遇到,哈哈開個玩笑~ 玩歸玩,鬧歸鬧,經(jīng)驗必須要老到!????
一、介紹
今天就給大家講講怎么處理數(shù)據(jù)表的壞塊情況!
對于 Oracle 數(shù)據(jù)塊物理損壞的情形,通常可以分為兩種情況:
- 有備份,通過 RMAN 恢復
- 無備份,通過 DBMS_REPAIR 修復
1、RMAN
有備份的情況下,這是很理想的情形,我們可以直接通過 RMAN 塊介質恢復(BLOCK MEDIA RECOVERY)功能來完成受損塊的恢復。
這里我是不建議恢復整個數(shù)據(jù)庫或者數(shù)據(jù)庫文件來修復這些少量受損的數(shù)據(jù)塊,有點浪費時間。
可參考官方文檔:Block Media Recovery with RMAN
2、DBMS_REPAIR
那如果沒有任何備份怎么辦? (PS:備份大于一切!)
我們可以使用 Oracle 自帶的 DBMS_REPAIR 包來實現(xiàn)修復。
?? 注意: 使用 DBMS_REPAIR 包來修復,并非完全恢復,而是標記壞塊,然后不對起進行訪問,這部分被標記的數(shù)據(jù)也就丟失了,這是無法避免的。
可參考MOS文檔:DBMS_REPAIR SCRIPT (Doc ID 556733.1)
二、實戰(zhàn)環(huán)境準備
1、環(huán)境安裝
使用我編寫的一鍵安裝腳本創(chuàng)建:
cd /Volumes/DBA/voracle/github/single_db
vagrant up
vagrant ssh

2、測試數(shù)據(jù)準備
創(chuàng)建表空間:
create tablespace eason datafile '/oradata/orcl/eason.dbf' size 1g autoextend on;

創(chuàng)建用戶:
create user eason identified by eason default tablespace eason;
grant dba to eason;

創(chuàng)建測試表:
create table hyj as select * from dba_objects;

創(chuàng)建表索引:
create index i_hyj on hyj(object_id);

3、查看表相關信息
查看表段上的相關信息:
select segment_name , header_file , header_block,blocks from dba_segments where segment_name ='HYJ';

查出包含行記錄的數(shù)據(jù)塊:
select distinct dbms_rowid.rowid_block_number(rowid) from eason.hyj order by 1;
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------------
1411
1412
1413
...
...
...
2665
2666
2667
1232 rows selected.

select * from dba_extents where segment_name='HYJ';

?? 注意: 這里看到 HEADER_BLOCK 和 BLOCK_ID 不一致,其實一個 segment 的第一個區(qū)的第一個塊是 FIRST LEVEL BITMAP BLOCK,第二個塊是 SECOND LEVEL BITMAP BLOCK,這兩個塊是用來管理 free block 的,第三個塊是 PAGETABLE SEGMENT HEADER,這個塊才是 segment 里的 HEADER_BLOCK。
4、RMAN 備份
首先,我們先做一個全備份,用來演示 RMAN 修復壞塊!
run {
allocate channel c1 device type disk;
allocate channel c2 device type disk;
crosscheck backup;
crosscheck archivelog all;
sql"alter system switch logfile";
delete noprompt expired backup;
delete noprompt obsolete device type disk;
backup database include current controlfile format '/backup/backlv_%d_%T_%t_%s_%p';
backup archivelog all DELETE INPUT;
release channel c1;
release channel c2;
}

5、模擬壞塊
破壞 1468、1688、2468 數(shù)據(jù)塊的內(nèi)容:
dd if=/dev/zero of=/oradata/orcl/eason.dbf bs=8192 conv=notrunc seek=1468 count=1
dd if=/dev/zero of=/oradata/orcl/eason.dbf bs=8192 conv=notrunc seek=1688 count=1
dd if=/dev/zero of=/oradata/orcl/eason.dbf bs=8192 conv=notrunc seek=2468 count=1

清除 buffer cache 的內(nèi)容:
alter system flush buffer_cache;

再次查詢表 hyj,此時查詢已經(jīng)報錯,發(fā)現(xiàn)有壞塊:
select * from eason.hyj;

當然,也可以使用 bbed 進行壞塊模擬!
6、壞塊檢查
下面在介紹幾種發(fā)現(xiàn)壞塊的方式:
1、使用 DBV 檢查當前文件的壞塊:
dbv file=/oradata/orcl/eason.dbf blocksize=8192;

使用 DBV檢查,同樣發(fā)現(xiàn)了壞塊!
2、使用 rman 檢查數(shù)據(jù)庫壞塊:
## 檢查對應的數(shù)據(jù)文件
backup check logical validate datafile 5;
## 檢查當前數(shù)據(jù)庫
backup validate check logical database;

結合 V$DATABASE_BLOCK_CORRUPTION 視圖查看,更加方便:
select * from V$DATABASE_BLOCK_CORRUPTION;

使用 RMAN 檢查后,同樣發(fā)現(xiàn)了壞塊!
3、通過數(shù)據(jù)庫的告警日志也可以發(fā)現(xiàn)報錯:

4、通過報錯信息快照查找對應的壞表,依次填寫數(shù)據(jù)文件 ID 5 和 壞塊 ID 1468:
SELECT tablespace_name, segment_type, owner, segment_name
FROM dba_extents
WHERE file_id = &fileid
and &blockid between block_id AND block_id + blocks - 1;

實驗環(huán)境準備完畢,下面開始實戰(zhàn)!
三、實戰(zhàn)演示
今天,我打算使用上述介紹的 2 種方式來演示!
1、RMAN 修復
由于我們之前已經(jīng)備份了,因此直接使用備份來恢復壞塊:
blockrecover datafile 5 block 1468;

blockrecover datafile 5 block 1688,2468;

使用同樣的方式,依次修復壞塊 1688,2468,修復成功后,查詢已恢復正常!
再次檢查壞塊情況:
backup validate check logical database;
select * from V$DATABASE_BLOCK_CORRUPTION;

壞塊已經(jīng)都被恢復,并且數(shù)據(jù)沒有丟失!
2、DBMS_REPAIR 修復
首先,依然使用 dd 先模擬壞塊:
dd if=/dev/zero of=/oradata/orcl/eason.dbf bs=8192 conv=notrunc seek=3333 count=1
dd if=/dev/zero of=/oradata/orcl/eason.dbf bs=8192 conv=notrunc seek=3368 count=1
dd if=/dev/zero of=/oradata/orcl/eason.dbf bs=8192 conv=notrunc seek=4000 count=1

在沒有備份的前提下,我們就無法做到無損修復壞塊了,需要損失對應壞塊的數(shù)據(jù)。
1、創(chuàng)建 repair 表,用于記錄需要被修復的表:
begin
dbms_repair.admin_tables (
table_name => 'REPAIR_TABLE',
table_type => dbms_repair.repair_table,
action => dbms_repair.create_action,
tablespace => 'USERS');
end;
/

2、創(chuàng)建 Orphan Key 表,用于記錄在表塊損壞后那些孤立索引,也就是指向壞塊的那些索引 :
begin
dbms_repair.admin_tables (
table_name => 'ORPHAN_KEY_TABLE',
table_type => dbms_repair.orphan_table,
action => dbms_repair.create_action,
tablespace => 'USERS');
end;
/

3、檢查壞塊,檢測對象上受損的情形,并返回受損塊數(shù)為 3:
declare
num_corrupt int;
begin
num_corrupt := 0;
DBMS_REPAIR.CHECK_OBJECT (
schema_name =>'EASON',
object_name =>'HYJ',
repair_table_name =>'REPAIR_TABLE',
corrupt_count =>num_corrupt);
dbms_output.put_line('number corrupt:' || to_char(num_corrupt));
end;
/

4、查看受損的塊信息:
select object_name, block_id, corrupt_type, marked_corrupt, repair_description from repair_table;

?? 注意: 這里 marked_corrupt 被標記為 TRUE,應該是系統(tǒng)在執(zhí)行 CHECK_OBJECT 過程中自動完成了FIX_CORRUPT_BLOCKS。如果被標記為 FALSE,需要再運行 FIX_CORRUPT_BLOCKS 來完成壞塊的標記工作。
5、修復被損壞的數(shù)據(jù)塊,這些被損壞的數(shù)據(jù)塊是在執(zhí)行了 check_object 之后生成的:
declare
cc number;
begin
dbms_repair.fix_corrupt_blocks(schema_name => 'EASON',
object_name => 'HYJ',
fix_count => cc);
dbms_output.put_line('Number of blocks fixed: ' || to_char(cc));
end;
/

標記了 0 個壞塊,說明 CHECK_OBJECT 完成了標記工作。
6、使用 DUMP_ORPHAN_KEYS 過程將那些指向壞塊的索引鍵值填充到 ORPHAN_KEY_TABLE:
declare
cc number;
begin
dbms_repair.dump_orphan_keys
(
schema_name => 'EASON',
object_name => 'I_HYJ',
object_type => dbms_repair.index_object,
repair_table_name => 'REPAIR_TABLE',
orphan_table_name=> 'ORPHAN_KEY_TABLE',
key_count => cc
);
dbms_output.put_line('Number of orphan keys: ' || to_char(cc));
end;
/

表明 202 條記錄被損壞丟失!
?? 注意: 此處一定要注意 object_name 是索引名,而不是 table_name,這里 dump 的是損壞的索引信息.如果表有多個索引,需要為每個索引執(zhí)行 DUMP_ORPHAN_KEYS 操作。
7、驗證對象是否可以查詢,下面的結果顯示依舊無法查詢:
select count(*) from eason.hyj;

8、跳過壞塊:
BEGIN
DBMS_REPAIR.SKIP_CORRUPT_BLOCKS (
SCHEMA_NAME => 'EASON',
OBJECT_NAME => 'HYJ',
OBJECT_TYPE => dbms_repair.table_object,
FLAGS => dbms_repair.skip_flag);
END;
/

?? 注意: 丟失 202 條記錄,丟失記錄的 rowid 可以在 ORPHAN_KEY_TABLE 表中找到。
9、重建索引:
alter index eason.I_HYJ rebuild;

10、驗證結果

至此,表中數(shù)據(jù)可以順利被訪問!
由于壞塊并沒有消失,而是被標記跳過,因此還是可以查看壞塊:
select * from V$DATABASE_BLOCK_CORRUPTION;

用這種方法可以找回部分數(shù)據(jù),也可以找回建了索引的值,但是使用dbv再檢查表空間的數(shù)據(jù)文件時,仍然會顯示有損壞的數(shù)據(jù)塊。
這時需要把表的數(shù)據(jù)全部導出,再重建表或者表空間,然后再把找回的數(shù)據(jù)導入數(shù)據(jù)庫,推薦用 expdp/impdp 命令做,可以徹底消除 dbv 檢查到的壞塊。
寫在最后
備份大于一切,也是最后的防線,所以請大家一定要做好備份!886
往期精彩文章
Oracle 一鍵巡檢自動生成 Word 報告
Oracle 一鍵安裝合集
Oracle一鍵安裝腳本的 21 個疑問與解答
Oracle一鍵巡檢腳本的 21 個疑問與解答
全網(wǎng)首發(fā):Oracle 23ai 一鍵安裝腳本(非 RPM)
Oracle 19C 最新 RU 補丁 19.24 ,一鍵安裝!
Oracle Linux 7.9 一鍵安裝 Oracle 19C
RedHat 9.4(aarch64) 一鍵安裝 Oracle 19C
openEuler 22.03 LTS SP4 一鍵安裝 Oracle 19C RAC
RHEL 7.9 一鍵安裝 Oracle 19C 19.23 RAC
Oracle DataGuard GAP 修復手冊
優(yōu)化 Oracle:最佳實踐與開發(fā)規(guī)范
DBA 必備:Linux 軟件源配置全攻略
Linux 一鍵配置時鐘同步全攻略





