Skip to main content

Erreur ORA-00600 - index, ou blocs, ou lob corrompu

On trouve des erreurs ORA-0600 dans l'alert.log. Il peut s'agir de blocs disque corrompus suite à un problème d'accès disque, ou d'un index désynchronisé, ou d'un LOB corrompu dans la table (liste non exhaustive !).
ORA-00600: internal error code, arguments: [13011], [117675], [32437212], [0], [32705125], [0], [], [], [], [], [], []

NOTE : il existe un décodeur d'ORA-00600 sur Metalink :
https://metalink.oracle.com/metalink/plsql/ml2_documents.showDocument?p_database_id=NOT&p_id=153788.1

NOTE 2 : Si l'erreur ORA-00600 ne génère pas des codes décodables, en général le fichier trace associé à l'erreur 0600 permet de trouver la requête, donc la table, affectée.

Trouver la table concernée par le bloc corrompu

Trouver le fichier et la table concernée (voir FIX ORA-00600 (1) pour la liste des paramètres de l'erreur 0600):

SELECT owner, object_name, object_type, object_id, data_object_id
FROM dba_objects
WHERE data_object_id = 117675;

ANALYZE et VALIDATE STRUCTURE

D'abord analyser la table :

analyze table myschema.mytable validate structure cascade;

L'analyse de la table ne renvoie pas d'erreur

Si la commande ne renvoie pas d'erreur (00600), il faut faire de même avec ses indexes. Pour trouver les indexes :

select index_name from dba_indexes where owner='MYSCHEMA' and tble_name='MYTABLE';

Puis, pour chaque index trouvé, valider la structure :

analyze index MYSCHEMA.<MYTABLE_IND1> validate structure;

Si la table a trop d'index, on peut essayer de trouver lequel a un problème par :

SQL> alter session set max_dump_file_size = unlimited;
SQL> ALTER SESSION SET tracefile_identifier = 'mon_fichier_trace';
SQL> analyze table MYSCHEMA."MYTABLE" validate structure cascade into invalid_rows;

Dans 'mon_fichier_trace', on trouve une ligne de ce type :

 

row not found in index tsn: 1 rdba: 0x00817bfa

On recherche l'index correspondant à "00817bfa" :

SELECT owner, segment_name, segment_type, partition_name
FROM DBA_SEGMENTS
WHERE header_file = (SELECT file#
FROM v$datafile
WHERE rfile# =
dbms_utility.data_block_address_file(
to_number('00817bfa','XXXXXXXX'))
AND ts#= 1)
AND header_block = dbms_utility.data_block_address_block(
to_number('00817bfa','XXXXXXXX'));

Sinon, on peut essayer de générer un script pour les analyser tous :

select 'analyze index '||owner||'.'||index_name||' validate structure' from dba_indexes where owner='MYSCHEMA' and table_name='MYTABLE';

L'analyse de la table renvoie des erreurs ORA-00600 ou ORA-01499 (échec de référence croisée à la table/index - voir fichier de trace)

SQL> analyze table myschema.mytable validate structure cascade;
analyze table myschema.mytable validate structure cascade
*
ERROR at line 1:
ORA-01499: table/index cross reference failure - see trace file

la première chose à faire est de valider (par DB Verify : utilitaire "dbv") que ce n'était pas un problème physique sur le fichier du tablespace. Trouver le tablespace et le fichier contenant la table :

SQL> select tablespace_name from dba_tables where table_name='MYTABLE';
TABLESPACE_NAME
------------------------------
MYTBLSPCE

SQL> select file_name from dba_data_files where tablespace_name='MYTBLSPCE';
FILE_NAME
---------------------------------
/u01/ORADATA/MYDB/MYTBLSPCE01.DBF

SQL> show parameter db_block_size
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_block_size                        integer     8192

Lancer dbv :

$ dbv file='MYTBLSPCE01.DBF' blocksize='8192'
DBVERIFY: Release 11.2.0.4.0 - Production on Fri Apr 26 16:35:54 2019
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.
DBVERIFY - Verification starting : FILE = /u01/ORADATA/MYDB/MYTBLSPCE01.DBF

DBVERIFY - Verification complete

Total Pages Examined         : 3440640
Total Pages Processed (Data) : 3102612
Total Pages Failing   (Data) : 0
Total Pages Processed (Index): 320
Total Pages Failing   (Index): 0
Total Pages Processed (Other): 139223
Total Pages Processed (Seg)  : 0
Total Pages Failing   (Seg)  : 0
Total Pages Empty            : 198485
Total Pages Marked Corrupt   : 0
Total Pages Influx           : 0
Total Pages Encrypted        : 0
Highest block SCN            : 2784020020 (1.2784020020)

Ici analyse sans erreur, ce n'est donc pas une erreur physique, mais bien une désynchronisation d'un index ou un lob corrompu. On reconstruit donc les indexes.

Si dbv renvoie des erreurs, le fichier lui-même est physiquement corrompu sur disque ! Il n'y a alors pas d'autre moyen que de re-créer la base proprement sur un disque valide et réimporter la dernière sauvegarde viable.

Reconstruction des indexes

SQL> ALTER INDEX "MYSCHEMA"."MYTABLE_IND1" REBUILD;
Index altered.

Cependant, il peut arriver que le REBUILD ne suffise pas; où qu'on décide de recréer les index dans un autre tablespace par sécurité.

Re-création des indexes

SQL> set long 200000 pagesize 0
SQL> select dbms_metadata.get_ddl('INDEX','MYTABLE_IND1','MYSCHEMA') from dual;

DBMS_METADATA.GET_DDL('INDEX','MYTABLE_IND1','MYSCHEMA')
-------------------------------------------------------------------------------

  CREATE INDEX "MYSCHEMA"."MYTABLE_IND1" ON "MYSCHEMA"."MYTABLE" ("COL1")
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "MYTBLSPCE"

SQL> ALTER INDEX "MYSCHEMA"."MYTABLE_IND1" UNUSABLE;
Index altered.

SQL> DROP INDEX "MYSCHEMA"."MYTABLE_IND1";
Index dropped.

SQL> CREATE INDEX "MYSCHEMA"."MYTABLE_IND1" ON "MYSCHEMA"."MYTABLE" ("COL1")
Index created.

Si l'index ne peut pas être recréé; exemple :

SQL> drop index "MYSCHEMA"."MYTABLE_IND1";
drop index "MYSCHEMA"."MYTABLE_IND1"
                *
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key

alors il faut le reconstruire avec REBUILD, en espérant que ça suffira; ou le déplacer (il sera reconstruit par Oracle à la destination).

LOB : déplacer le LOB dans un autre tablespace

Si les indexes sont reconstruits sans erreurs mais que la table continue à générer des erreurs, et qu'elle contient des colonnes LOB, il peut s'agir d'un LOB corrompu. Dans ce cas, déplacer le LOB pour le reconstruire (voir Déplacer un LOB dans un autre tablespace)

Dernier recours : dupliquer la table en excluant les blocs corrompus

Le code suivant permet de dupliquer la table "tab1" vers une table "tab1_new" sans les lignes illisibles :

REM Create a new table based on the table that is producing errors with no rows:
create table MYSCHEMA.MYTABLE_NEW
as
select * 
from MYSCHEMA.MYTABLE -- eventually, can add : "partition(<PARTITION_NUM>)"
where  1=2;

REM Create the table "bad_rows" to keep track of ROWIDs pointing to affected rows:
create table bad_rows (row_id rowid
                      ,oracle_error_code number);
set serveroutput on
DECLARE
  TYPE RowIDTab IS TABLE OF ROWID INDEX BY BINARY_INTEGER;
  CURSOR c1 IS select /*+ index(tab1) */ rowid
  from MYSCHEMA.MYTABLE tab1 -- eventually, can add : "partition(<PARTITION_NUM>)"
  -- eventually add a filter to exclude others not necessary rows. Ex : where IMEI is NOT NULL;
  ;
  r RowIDTab;
  rows NATURAL := 20000;
  bad_rows number := 0 ;
  errors number;
  error_code number;
  myrowid rowid;
BEGIN
  OPEN c1;
  LOOP
   FETCH c1 BULK COLLECT INTO r LIMIT rows;
   EXIT WHEN r.count=0;
   BEGIN
    FORALL i IN r.FIRST..r.LAST SAVE EXCEPTIONS
     insert into MYSCHEMA.MYTABLE_NEW 
     select /*+ ROWID(A) */ *
     from MYSCHEMA.MYTABLE A -- eventually, can add : "partition(<PARTITION_NUM>)"
     where rowid = r(i);
   EXCEPTION
   when OTHERS then
    BEGIN
     errors := SQL%BULK_EXCEPTIONS.COUNT;
     FOR err1 IN 1..errors LOOP
      error_code := SQL%BULK_EXCEPTIONS(err1).ERROR_CODE;
      if error_code in (1410, 8103, 1578) then
       myrowid := r(SQL%BULK_EXCEPTIONS(err1).ERROR_INDEX);
       bad_rows := bad_rows + 1;
       insert into bad_rows values(myrowid, error_code);
      else
       raise;
      end if;
     END LOOP;
    END;
   END;
   commit;
  END LOOP;
  commit;
  CLOSE c1;
  dbms_output.put_line('Total Bad Rows: '||bad_rows);
END;
/

Ressources

FIX ORA-0600 (1) : http://dmbadi.blogspot.com/2019/06/how-to-fix-ora-600-12700-internal-error.html
FIX ORA-0600 (2) : https://oracle-base.com/articles/misc/detect-and-correct-corruption
DBMS_REPAIR : https://www.jobacle.nl/?p=746
DUMP_ORPHAN_KEYS, SKIP_CORRUPT_BLOCKS : http://www.dba-oracle.com/concepts/dump_orphan_keys.htm
DBMS_REPAIR.FIX_CORRUPT_BLOCK : http://www.dba-oracle.com/t_fix_corrupt_blocks.htm
DB_ULTRA_SAFE, DB_BLOCK_CHECK* : http://www.dba-oracle.com/t_db_block_checking.htm
TOUT + DBV : http://www.datadisk.co.uk/html_docs/oracle/db_corruption.htm