oracle壞塊

文章出處:http://space.itpub.net/25472150/viewspace-688629   感謝作者的分享

 

Oracle數據庫出現壞塊現象是指:在Oracle數據庫的一個或多個數據塊(一個數據塊的容量在創建數據庫時由db_block_size參數指定,缺省爲8K)內出現內容混亂的現象。由於正常的數據塊都有固定的合法內容格式,壞塊的出現,導致數據庫進程無法正常解析數據塊的內容,進而使數據庫進程報錯乃至掛起,並級聯導致整個數據庫實例出現異常。

 

一.壞塊的產生原因

 

壞塊產生的原因大致有以下幾種:

1.1硬件問題

Oracle進程在處理一個數據塊時,首先將其讀入物理內存空間,在處理完成後,再由特定進程將其寫回磁盤;如果在這個過程中,出現內存故障,CPU計算失誤,都會導致內存數據塊的內容混亂,最後反映到寫回磁盤的數據塊內容有誤。同樣,如果存儲子系統出現異常,數據塊損壞也就隨之出現了。

 

1.2操作系統BUG

由於Oracle進程對數據塊的讀寫,都是以操作系統內核調用(system call)的方式完成的,如果操作系統在內核調用存在問題,必然導致Oracle進程寫入非法的內容。

 

1.3操作系統的I/O錯誤或緩衝問題

 

1.4內存或paging問題

 

Oracle軟件BUG

Oracle軟件特定版本上,可能出現導致數據塊的內容出現異常BUG。

 

1.5Oracle進程擾亂Oracle共享內存區域

如上文所述,在當數據塊的內容被讀入主機的物理內存時,如果其他非Oracle進程,對Oracle使用的共享內存區域形成了擾亂,最終導致寫回磁盤的數據塊內容混亂。

 

1.6異常關機,掉電,終止服務

異常關機,掉電,終止服務使進程異常終止,而破壞數據塊的完整性,導致壞塊產生。

注:這也是爲什麼突然斷電會導致數據庫無法啓動

 

由上可見,壞塊的形成原因複雜。當出現壞塊時,爲了找到確切的原因,需要大量的分析時間和排查操作,甚至需要多次重現才能找出根本原因。但當故障發生在生產系統上,我們爲了減少停機時間,會盡快實施應急權變措施以保證系統的可用性,這樣就破壞了故障現場,對根本原因的分析因而也更加困難了。

 

二.壞塊的預防

 

壞塊問題破壞性大,但並非不可預防。

 

2.1在Metalink.oracle.com網站,Oracle定期發佈基於特定軟件版本的“已知問題(known issues)說明”。對於可能導致壞塊的Oracle軟件BUG,在Oracle公司內部,是作爲高嚴重級別的問題進行處理,在“已知問題(known issues)說明”中,這些BUG以嚴重(Noticable)問題標出(標記爲*或+),部分問題,Oracle還會發布警告(Alert)通告。在文檔中,Oracle會提供相應的補丁或應對措施。

 

2.2Oracle提供備份恢復工具-Recovery Manager,提供了掃描文件檢查壞塊的功能。

在Recovery Manager界面中,使用:

RMAN> BACKUP CHECK LOGICAL VALIDATE DATAFILE n ;

 

可以檢查數據文件是否包含壞塊,同時並不產生實際的備份輸出。

 

2.3Dbv工具檢查

注:因爲dbv要求file後面跟的必須是一個文件擴展名,所以如果用裸設備存儲

的,就必須使用ln鏈接裸設備到一個文件,然後再用dbv對這個鏈接文件進行檢

查。

 

ANALYZE TABLE tablename VALIDATE STRUCTURE CASCADE

它執行壞塊的檢查,但是不會標記壞塊爲corrupt,檢測的結果保存在USER_DUMP_DEST目錄下的用戶trace文件中。

 

2.4利用exp工具導出整個數據庫可以檢測壞塊

對以下情況的壞塊是檢測不出來的:

 HWM以上的壞塊是不會發現的

 索引中存在的壞塊是不會發現的

 數據字典中的壞塊是不會發現的

 

結合數據庫性能綜合考慮db_block_checksum和db_blockchecking參數。

 

當我們使用Recovery Manager進行實際的數據庫備份時,同時也就進行了壞塊

檢查。但要注意的是,在線使用Recovery Manager掃描壞塊和備份時,需要數

據庫運行在歸檔模式(archive log),否則只能在數據庫未打開的情況下進行。

 

對於操作系統問題和硬件故障,則需要相應廠商的配合支持。同時,避免在數

據庫主機運行其他用戶進程,避免異常停機,也會減少壞塊發生的機率。

 

壞塊故障的識別

遇到壞塊問題時,數據庫的異常表現通常有:

報告ORA-01578錯誤。

報告Ora-1110錯誤。

報告ORA-00600錯誤,其中,第一個參數爲2000-8000,Cache layer 2000 – 4000,Transaction layer 4000 – 6000,Data layer 6000 - 8000。

Trace文件中出現Corrupt block dba: 0x160c5958 . found。

分析對象失敗。

後臺進程,如DBWR,LGWR出現長時間異常等待,如“LGWR wait for redo copy”。

 

 

四.Oracle數據塊損壞恢復總結

 

可以用DBV命令來檢測是否有壞塊:

 

在恢復前使用DBV命令檢查數據文件是否存在壞塊

dbv file=d:\oracle\oradata\mydb\RONLY.DBF blocksize=8192

 

查看數據壞塊所在數據文件號及塊號可以對錶進行一次全表掃描,如:

select count(*) from tablename;

 

 

 

4.1沒有備份的情況下:

4.1.1、使用exp/imp恢復

在這種情況下肯定會造成數據的丟失,在這種情況下應採取將數據導出然後重建表再進行導入的方法,來儘量恢復損壞數據塊中的數據,但是在有壞塊的情況下是不允許導出的,如下命令:

Exp test/test file=t.dmp tables=t;

 

導出命令在執行中會報ORA-01578錯誤,在這錯誤提示中會提示那個文件號的文件以及這個文件中的哪個塊被損壞,如:ORA—01578:ORACLE數據塊損壞(文件號4,塊號35)

針對以上的提示首先查詢那些對象被損壞

 

Select tablespace_name,segment_type,owner,segment_name From dba_extents Where file_id=4 and 35 between block_id and block_id+blocks-1;

 

如果被損壞的塊是索引,通常可以通過索引重建來解決,如果損壞的是數據(segment_type爲table),那麼通過設置如下內部事件使Exp操作跳過壞塊

Alter session set events=’10231 trace name context forever,level 10’;

 

然後重新執行導出命令,導出相關的表,然後執行Drop Table命令刪除相關表,之後重建表最後導入數據。

 

4.1.2、使用DBMS_REPAIR恢復

用DBMS_REPAIR當然也會丟失數據。這裏不做詳細的介紹,有興趣的可以查看oracle的在線文檔

 

 

4.2使用Rman進行恢復:

首先要存在Rman的最新備份集,然後執行如下命令:

RMAN>backup validate datafile 4;

 

檢查4號數據文件是否存在壞塊

執行查詢:

select * from v$database_block_corruption where file#=4;

 

如果4號文件存在壞塊的話,那麼將在結果集中有所顯示,會顯示損壞的塊號,根據顯示結果執行如下命令進行恢復:

RMAN>blockrecover datafile 4 block 35 from backupset;

 

該命令執行後即可恢復壞塊,並且不會造成數據丟失,但是要求數據庫必須要運行在歸檔模式下,否則RMAN無法發揮作用,而且通過RMAN做過最新的數據庫備份

 

 

4.3使用bbed恢復

使用bbed恢復時必須有數據文件的拷貝。

bbed就是英文block browse edit的縮寫,用來直接查看和修改數據文件數據的一個工具。

 

BBEDwindows 8i中在$ORACLE_HOME/bin下可以找到,9i中似乎未隨軟件發佈,故

windows沒有這個工具linux下需要編譯:

然後把$ORACLE_HOME/rdbms/lib加到環境變量的PATH裏面,就可以直接在命令中bbed了。

BBED的缺省口令爲blockedit,

For Oracle Internal Use only請謹慎使用Oracle不做技術支持。

 

 

五.如何查找壞塊所含的數據表名稱和數據的rowid

5.1.首先肯定知道那個數據文件壞了,查出該文件的file_id,relative_fno,tablespace_name

 

 利用dba_data_files可以查詢file_id(整個數據庫唯一序號),RELATIVE_FNO(相對一個表空間內的序號)

 

5.2.找到壞塊的ID(可以運行dbverify實現),假設找到的壞塊ID1234

5.3.運行下面的查詢,根據,壞塊的file_id,block id查找該塊對應的owner,segment_type,

segment_name等信息

 

   select owner,file_id,segment_name, segment_type, block_id, blocks

  from dba_extents

  where file_id=13 and block_id<=1234 and (block_id + blocks- 1) >= 1234;

 

 

5.4.根據壞塊的file_id,owner,segment_name,block_id,如果是數據表的話,用下面的查詢來得到對應壞塊的rowid

 

 假設owner : DAVE

 

     segment_name: BL

 

     file_id    : 13

 

     block_id   : 162

 

 運行下面的查詢來獲得該塊所含的rowid(如果沒有索引,可能就不能用下面的方式了):

 

select /*+ index(DAVE, i_test)*/ rowid

 from DAVE.BL

 where dbms_rowid.rowid_to_absolute_fno(rowid,'DAVE','BL')=13

 and dbms_rowid.rowid_block_number(rowid)=162;  

 

六,如何模擬壞塊

 

DBA的基本知識,製造壞塊的方法很多的,可以用ultraedit,也可以用dd命令,同時也呆以用orapatch工具

 

6.1 orapatch工具:
$orapatch open tools001.dbf write
patch>set hex --
要用十六進制
patch>display 177 --orapatch以512字節爲工作模式,假定想破壞第11個block即爲:8k/512*11+1(file header)
patch>find 00400003 --選一個要編輯的點

patch>modify 00400002 --破壞
patch>exit

 

6.2用編輯器打開datafile8192字節爲一大小(dbblock8192)
下面是一個block的開始的20個字節和結尾的4個字節
06 02 00 00 08 00 c0 02 6c 43 0d 00 00 00 01 02
00 00 00 00 -- block head

06 02 6c 43 -- block tail
這裏有scn(6c 43)的情況和block (06)的類型(01)和head的seq: 0x01對應,只要改block尾部的4個字節中的或block開始的對應字節任何一個就一定會有ora-1578,但如果數據庫有ora-1578的error不一定是這樣引起的

btw:UE不是很好用winhex吧不錯喔

 

6.3 BBED工具

 

 



七. 如何利用dbms_repair來標記和跳過壞塊

但是當數據量很大,或7*24的系統時,我們使用dbms_repair來處理。dbms_repair是從oracle8i開始提供的。

 

準備工作:

createtablespaceblockdatafile'/u01/block.dbf'size5M;

createtableDMMtablespaceblockasselect*fromall_tables;

commit;

CREATEINDEXindx_dmmonDMM(TABLE_NAME);

selectcount(*)fromDMM;

 COUNT(*)

----------

12896

 

 

7.1.創建管理表:

SQL> conn sys/admin as sysdba;

已連接。

SQL> exec DBMS_REPAIR.ADMIN_TABLES('REPAIR_TABLE',1,1,'USERS');

PL/SQL procedure successfully completed

SQL> exec DBMS_REPAIR.ADMIN_TABLES('ORPHAN_TABLE',2,1,'USERS');

PL/SQL procedure successfully completed

 

7.2.檢查壞塊:dbms_repair.check_object

 

/* Formatted on 2009-12-16 23:41:32 (QP5 v5.115.810.9015) */

Set serveroutput on;

DECLARE

  cc NUMBER;

BEGIN

  DBMS_REPAIR.check_object(schema_name=>'SYS',--注意此處是用戶名

                            object_name    =>'DMM',

                            corrupt_count  =>cc);

  DBMS_OUTPUT.put_line(TO_CHAR(cc));

END;

 

正常情況下輸入爲0.

 

如果有壞塊,可以在創建的REPAIR_TABLE中查看塊損壞信息:   

 

/* Formatted on 2009-12-17 13:18:19 (QP5 v5.115.810.9015) */

SELECT  object_name,

        relative_file_id,

        block_id,

        marked_corrupt,

        corrupt_description,

        repair_description,

        CHECK_TIMESTAMP

 FROM  repair_table;                                                                                                                                                                                                       

                                                                                                                                           

 

注意:在8i下,check_object只會檢查壞塊,MARKED_CORRUPT爲false,故需要執行第三步:定位壞塊,fix_corrupt_blocks定位,修改MARKED_CORRUPT爲true,同時更新CHECK_TIMESTAMP。9i以後經過check_object,MARKED_CORRUPT的值已經標識爲TRUE了。所以可以直接進行第四步了。

 

 

7.3.定位壞塊:dbms_repair.fix_corrupt_blocks     

只有將壞塊信息寫入定義的REPAIR_TABLE後,才能定位壞塊。 

/* Formatted on 2009-12-17 13:29:01 (QP5 v5.115.810.9015) */

DECLARE

  cc  NUMBER;

BEGIN

  DBMS_REPAIR.fix_corrupt_blocks(schema_name  =>'SYS',

                                  object_name  =>'DMM',

                                  fix_count    =>cc);

  DBMS_OUTPUT.put_line(a=>TO_CHAR(cc));

END;

 

 

7.4.跳過壞塊:

我們前面雖然定位了壞塊,但是,如果我們訪問table:

SQL> select count(*) fromSYS.DMM;

 

 

ORA-01578: ORACLE數據塊損壞(文件號14,塊號154)

ORA-01110:數據文件14: 'D: \BLOCK.DBF'

 

還是會得到錯誤信息。這裏需要用skip_corrupt_blocks來跳過壞塊:

 

/* Formatted on 2009-12-17 13:30:17 (QP5 v5.115.810.9015) */

execdbms_repair.skip_corrupt_blocks(schema_name=>'SYS',object_name=>'DMM',flags=>1);

 

 

SQL> select count(*) from SYS.DMM;

 

 COUNT(*)

----------

    12850

丟失了12896-12850=46行數據。

 

 

7.5.處理index上的無效鍵值;dump_orphan_keys

 

/* Formatted on 2009-12-17 13:34:55 (QP5 v5.115.810.9015) */

DECLARE

  cc  NUMBER;

BEGIN

  DBMS_REPAIR.dump_orphan_keys(schema_name        =>'SYS',

                                object_name        =>'INDX_DMM',

                                object_type        =>2,

                                repair_table_name  =>'REPAIR_TABLE',

                                orphan_table_name  =>'ORPHAN_TABLE',

                                key_count          =>CC);

END;

 

通過以下命令可以知道丟失行的信息:

SQL> SELECT * FROM ORPHAN_TABLE;

 

我們根據這個結果來考慮是否需要rebuild index.

7.6.重建freelistrebuild_freelists

 

/* Formatted on 2009-12-17 13:37:57 (QP5 v5.115.810.9015) */

execdbms_repair.rebuild_freelists(schema_name=>'SYS',object_name=>'DMM');

 

八.設置內部事件使exp跳過壞塊

我們可以用設置event的方法來處理壞塊:先模擬出壞塊,然後用dbv檢查,此時,不用dbms_repair,而用下面的方法:

 

8.1.exp該表試驗一下

在這種情況下,如果有備份,需要從備份中恢復,如果沒有備份,那麼壞塊部分的數據肯定要丟失了

在這個時候導出是不允許的:
E:\>exp system/admin file=t.dmp tables=t

報錯如下:
即將導出指定的表通過常規路徑...
. .正在導出表
T
EXP-00056:遇到ORACLE錯誤
1578
ORA-01578: ORACLE數據塊損壞(文件號4,塊號35)

ORA-01110:數據文件4: 'E:\ORACLE\ORADATA\EYGLE\BLOCK.DBF'
導出成功終止,但出現警告。

 

8.2.對於不同的情況需要區別對待,如果損失不是數據而是重要的oracle內部信息,則不能用set event

 

首先你需要檢查損壞的對象,使用以下SQL

/* Formatted on 2009-12-17 14:00:42 (QP5 v5.115.810.9015) */

SELECT  tablespace_name,

        segment_type,

        owner,

        segment_name

 FROM  dba_extents

 WHERE  file_id=4AND35BETWEENblock_idANDblock_id+blocks-1;




 


8.3.如果損失的是數據,ok,可以設置內部事件,使exp跳過這些損壞的block

 

10231事件指定數據庫在進行全表掃描時跳過損壞的塊

ALTER SYSTEM SET EVENTS='10231 trace name context forever,level 10' ;

然後我們可以導出未損壞的數據
E:\> exp system/admin file=t.dmp tables=t

即將導出指定的表通過常規路徑...
. .正在導出表T 8036行被導出

在沒有警告的情況下成功終止導出。

然後我們可以drop tablerecreate,然後導入數據


8.4.重建表,再導入

SQL> connect sys/admin as sysdba
已連接。

SQL> drop table t;
表已丟棄。
E:\>imp system/admin file=t.dmp tables=t

.
正在將system的對象導入到system
. .正在導入表"T" 8036行被導入

成功終止導入,但出現警告。

E:\>exit
SQL> select count(*) from t;

COUNT(*)
----------
8036

完成數據恢復.

 

損失的行數可以從2個行數相減計算:
8192 - 8036 = 156行數據

 

 

8.5如果要取消events設置,做以下操作:

1.如果你在初始化參數中設置的
註釋之

2.如果在命令行設置的
alter system set events='10231 trace name context off';


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章