Inhaltsbereich

Korrektur eines Oracle-Blockfehlers bei manueller Sicherung (also ohne RMAN)

Block Corruption ist eines der schlimmsten Dinge, die bei einer Oracle-Datenbank passieren können. Es gibt verschiedene Ursachen für das Auftreten einer Block-Corruption:

  • Hardware-Fehler
  • Betriebssystemfehler
  • Oraclefehler

Oracle-Blockfehler werden in der Alert-Log aufgezeichnet: Sie werden üblicherweise erst deutlich nach dem Auftreten reportet. Das kommt daher, dass Oracle den Fehler meistens nicht beim Schreiben erkennt, sondern erst beim Lesen.

Beispiel:

Corrupt block relative dba: 0x060079fd (file 24, block 31229)
Bad check value found during buffer read
Data in bad block -
type: 6 format: 2 rdba: 0x060079fd
last change scn: 0x0000.019bd0c8 seq: 0x1 flg: 0x06
consistency value in tail: 0xd0c80601
check value in block header: 0xc4cd, computed block checksum: 0x27ed
spare1: 0x0, spare2: 0x0, spare3: 0x

Bemerkung: Die FILE-ID ist die absolute File-Nummer: AFN bezeichnet. Die Block-Nummer wird mit BL bezeichet

 

Bestimmen des Speicherortes, der davon betroffen ist

In dem Beispiel oben wird die File-ID aufgeführt: Wenn die Datenbank noch geöffnet ist, dann folgende Abfrage absetzen:

SELECT name FROM v$datafile WHERE file#=24;
NAME
--------------------------------------------------------------------------------
G:\ORA00\ODBFS03\P205\DATA\INDEXES_NEU_06.DBF

Falls kein Treffer gefunden wird, handelt es sich nicht um ein Datenfile, sondern um ein Tempfile. Die File-Nummer, die oben in der Fehlermeldung angezeigt wird,ist die relative Filenummer (RFN):

SELECT t.name FROM v$tempfile t CROSS JOIN v$parameter v WHERE v.name = 'db_files' and file#=<WERT aus der Meldung> + value

Oder einfacher vielleicht:

SELECT tablespace_name, file_id + value "AFN", relative_fno "RFN" FROM dba_temp_files cross join v$parameter WHERE name='db_files';

Dann mit der relativen Filenummer in der v$tempfile suchen:

select name from v$tempfile where file#=<RFN>;

 

Kontrolle der Hardware

Überprüfung der Hardware. Hier schaut man z. B. in die Ereignisanzeige bei Windows. Wenn eine Festplatte unbrauchbar ist:

  1. Eine Liste erstellen mit allen betroffenen Files:
  2. Datenbank schliessen: shutdown immediate
  3. Alle Files wenn möglich auf einen anderen fehlerfreien Speicher mit Betriebssystemkommandos kopieren
  4. Datenbank mounten: startup mount
  5. Der Datenbank die neuen Speicherorte mitteilen:

ALTER DATABASE RENAME FILE '/oldLocation/myfile.dbf' TO '/newLocation/myfile.dbf';

6. Datenbank öffnen:

ALTER DATABASE OPEN;

 

 

Bestimmen des Objekts, das betroffen ist:

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;

Beispiel:

SELECT tablespace_name, segment_type, owner, segment_name
FROM dba_extents
WHERE file_id = 9
and 79957 between block_id AND block_id + blocks - 1

TABLESPACE_NAME                SEGMENT_TYPE       OWNER                          SEGMENT_NAME
------------------------------ ------------------ ------------------------------ -------------------
INDEXES_NEU                    INDEX              SAP                            IDX_BEGRU

 

5. Behebung des Fehlers

Die Behebung ist abhängig von dem Segmenttyp: Es gibt folgende Möglichkeiten:
a) CACHE 
b) CLUSTER 
c) INDEX PARTITION 
d) INDEX 
e) ROLLBACK 
f) TABLE PARTITION 
g) TABLE 
h) TEMPORARY 
i) SONSTWAS
a) CACHE Bitte Statement überprüfen, File-Id AFN und Block-Id BL Wenn erneut CACHE als Typ erscheint Oracle-Support kontaktieren
b) CLUSTER Wenn es ein CLUSTER ist, muss rausgefunden werden, welche Tabelle betroffen ist.</pre>

SELECT owner, table_name  FROM dba_tables WHERE owner='<SEGMENT-OWNER>' AND cluster_name='<SEGMENT-NAME>';

Wenn es sich um eine Dictionary-Tabelle handelt (OWNER = SYS) Media Recovery
durchführen, siehe unten.
c) INDEX PARTITION Zur Doku die Partition ermitteln:

SELECT partition_name  FROM dba_extents WHERE file_id = <AFN> AND <BL> BETWEEN block_id AND block_id + blocks - 1;

Ansonten verfahren wie unter d)
d) INDEX
Wenn der OWNER SYS ist, Oracle Support kontaktieren
Ansonsten: Tabelle ermitteln.

SELECT table_owner, table_name FROM dba_indexes WHERE owner='<SEGMENT-OWNER>' AND index_name='<SEGMENT-NAME>';

Beispiel:

SQL> SELECT table_owner, table_name FROM dba_indexes WHERE owner='SAP' and index_name ='IDX_BEGRU';
TABLE_OWNER                    TABLE_NAME
------------------------------ ------------------------------
SAP                            TB_BEGRU

Ermitteln, ob der Index Grundlage eines Constraintes ist.

SELECT owner, constraint_name, constraint_type, table_name FROM dba_constraints WHERE owner='<TABLE-OWNER>' AND constraint_name='<INDEX-NAME>';

Beispiel:

SELECT owner, constraint_name, constraint_type, table_name FROM dba_constraints WHERE owner='INFO' AND constraint_name='IDX_RECHNER_USERLOG';

Mögliche Werte für einen Constraint sind:
P: Primary Key
U: Unique Key
Wenn es sich um einen Primary Key handelt, dann prüfen, ob der Primary Key
von einem Foreign Key referenziert wird.

SELECT owner, constraint_name, constraint_type, table_name FROM dba_constraints WHERE r_owner='<TABLE-OWNER>' AND r_constraint_name='<INDEX-NAME>';

Wenn der OWNER SYS ist. Media Recovery durchführen.
Ansonsten: Den Index neu anlegen:
Zuerst das DDL ermitteln.
Dazu mit OWNER anmelden und folgendes Select absetzen:

SELECT DBMS_METADATA.GET_DDL('INDEX',<INDEX_NAME>) from dual;

Beispiel:
Die Rückgabe der Funktion ist vom Typ long, also den Output entsprechend groß machen.

SQL> set long 5000
SQL> SELECT DBMS_METADATA.GET_DDL('INDEX','IDX_RECHNER_USERLOG') from dual;
DBMS_METADATA.GET_DDL('INDEX',
-----------------------------------------------------------------------------
CREATE INDEX "SAP"."IDX_BEGRU" ON "SAP"."TB_BEGRU" ("EKP")
PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "INDEXES_NEU"

Hat der Index einen oder mehrere Foreign Key, dann diesen oder diese ausschalten.

ALTER TABLE <child_table> DISABLE CONSTRAINT <fk_constraint>;

Handelt es sich um einen Primary Key, so muss der Constraint ausgeschaltet werden.

ALTER TABLE <table> DISABLE CONSTRAINT <pk_constraint>;

Dann den Index löschen

drop index <index_name>;

Index neu anlegen:

create index ....

Evtl Primary Key Constraint wieder anlegen:

ALTER TABLE <table> ENABLE CONSTRAINT <pk_constraint>;

Evtl. Foreign Key Constraints aktivieren:

ALTER TABLE <child_table> ENABLE CONSTRAINT <fk_constraint>;

Bei einem INDEX PARTITION mit dem Befehl

ALTER INDEX ... REBUILD PARTITION ...;

den index neu anlegen.
Bem.: Auf keinem Fall den Index mit ALTER INDEX ... REBUILD neu anlegen, da hier
existierende Segmente wieder benutzt werden.
ALTER INDEX ... REBUILD ONLINE bzw, ALTER INDEX ... REBUILD PARTITION ...;
machen dies nicht.
Beheben des Fehlers mit folgendem Befehl:

ALTER INDEX xxx REBUILD PARTITION ppp;

e) ROLLBACK
Media Revovery.
f) TABLE PARTITION
Bestimmen der Partition und weiter verfahren wie unter g) TABLE

SELECT partition_name FROM dba_extents WHERE file_id = <AFN> AND <BL> BETWEEN block_id AND block_id + blocks - 1;

g) TABLE
Wenn der OWNER SYS ist, es sich also um eine Dictionary-Tabelle handelt, ist Media Recovery notwendig
Kann die Tabelle gedroppt und neu angelegt werden, ohne dass Daten aus der Tabelle selbst entnommen werden müssen?
->
Ermitteln aller Indizes:

SELECT owner, index_name, index_type FROM dba_indexes WHERE table_owner='<SEGMENT-OWNER>' AND table_name='<TABLE-NAME>';

Ermitteln ob ein Primary Key vorliegt:

SELECT owner, constraint_name, constraint_type, table_name FROM dba_constraints WHERE owner='<TABLE-OWNER>' AND table_name='<SEGMENT-NAME>' AND constraint_type='P';

Wenn es sich um einen Primary Key handelt, dann prüfen, ob der Primary Key
von einem Foreign Key referenziert wird.

SELECT owner, constraint_name, constraint_type, table_name FROM dba_constraints WHERE r_owner='<TABLE-OWNER>' AND r_constraint_name='<CONSTRAINT-NAME>';

Wenn der OWNER SYS ist, ist Media Recovery notwendig.
-> Abfolge analog Indizes
Foreign -Key-Constraints ausschalten
Indizes-DDL-erzeugen.
Tabelle-DDL-erzeugen.

set long 5000
SELECT DBMS_METADATA.GET_DDL('INDEX','<INDEX_NAME>') from dual;
SELECT DBMS_METADATA.GET_DDL('TABLE','<TABLE_NAME>') from dual;

Tabelle droppen (falls Kunde nicht 100% sicher, ob alle Daten wieder hergestellt werden
, Tabelle umbenennen)

rename <TABLE_NAME> to <TABLE_NAME>_bak; )drop table <table_name>;

Tabelle neu anlegen.
Indizes neu anlegen.
Foreign Key-Constraints aktivieren.
Wenn die Tabelle nicht gedroppt werden kann, dann Media Recovery
h) TEMPORARY
Handelt es sich um ein Temporary Tablespace, so ist nachzuschauen, welche User davon
betroffen sind.

select username from dba_users where TEMPORARY_TABLESPACE =<TABLESPACE_NAME>;
USERNAME
------------------------------
SCOTT

Anschliessend ist ein neues temporaeres Tablespace anzulegen, dieses
als default Tablespace zu bestimmen und die user diesem Tablespace zuzuweisen.
Beispiel:

CREATE TEMPORARY TABLESPACE temp_neu TEMPFILE 'oracle/oradata/xxxsidxxx/temp1.dbf' SIZE 512064K REUSE;
ALTER DATABASE DEFAULT TEMPORARY TABLESPACE temp_neu;
ALTER USER SCOTT TEMPORARY TABLESPACE temp_neu;

Wenn das alte Tablespace 4 GB groß ist dann natürlich das neue auch so gross anlegen.

Rechter Inhaltsbereich

eXirius IT Dienstleistungen GmbH
Juchem-Straße 24
66571 Eppelborn

Telefon: +49 (6881) 99 99 5 - 0
Fax:        +49 (6881) 99 99 5 - 77

E-Mail: info(at)exirius.de