Email: service@parnassusdata.com 7 x 24 online support!

Oracle ORA-600 [12700] が発生した場合の調査方法

Oracle ORA-600 [12700] が発生した場合の調査方法

 

 

 

ORACLEデータベース によくあるエラ の解決策

プロのOracle Databaseの復旧サービスを提供
携帯番号: +86 13764045638 メール:service@parnassusdata.com

 

[概要]

この文書ではOracleの内部エラーORA-600 [12700] が発生した場合にどのような
調査を行うかを紹介します。
ORA-600 [12700] は一般的に、索引では存在しているはずの行が表レベルでは存
在していないことを検知して発生するエラーです。


[対象リリース]
Oracle9i Database Release 2 までのすべてのリリース

* Oracle Database 10g Release 1 以降は ORA-600 [12700] エラーは 
  ORA-600 [kdsgrp1] エラーとして出力されます。
  あわせて以下技術文書を参照してください。
   Document 1752889.1(KROWN:148224) ORA-600[kdsgrp1] エラーの意味について


[対象プラットフォーム]
すべてのプラットフォーム


[詳細]
問題を診断するにあたり、以下のステップを確認します。
--------------------------------------------------------------------------------
-1- トレースファイルにORA-600[12700]が出力されているか確認
-2- データ・オブジェクト自体が壊れてしまっている可能性はあるかどうか確認
    2.1 データ・オブジェクトが壊れているかどうかのチェック
-3- 索引自体が壊れてしまっている可能性はあるか確認
-4- 索引が壊れていた場合、その壊れた索引を再作成することは可能か
-5- データが壊れていた場合にはDocument 28814.1を参照
-6- データも索引も壊れていない場合には読み取り一貫性の問題である可能性があります
-7- データや索引が壊れていて、さらなる分析が必要な場合
--------------------------------------------------------------------------------

-1- トレースファイルにORA-600[12700]が出力されているか確認
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ORA-600[12700]が発生すると、alert_<SID>.logファイルに以下のようなエントリが
出力されます。

Tue May 29 13:10:16 2001
Errors in file /users/ora816/rdbms/log/ora_6496.trc:
ORA-00600: internal error code, arguments: [12700], [2989], ....

この例ではORA-600[12700]のダンプを含んだトレースファイル ora_6496.trc 
が出力されます。


-2- データ・オブジェクト自体が壊れてしまっている可能性はあるかどうか確認
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ORA-600[12700]のトレースファイルは以下のような内容を含んでいます。

*** 2001-05-29 18:59:53.735
ksedmp: internal or fatal error
ORA-00600: internal error code, arguments: [12700], [2997], [16778259],... 
Current SQL statement for this session:
select * from t12700bis where a='yyy'
              ^^^^^^^^^

このケースでは表"t12700bis"に問題があります。

"Current SQL statement for this session:" はより複雑なSQL文を示す場合も
あります。

エラー発生時に実行していたSQL文から壊れているデータオブジェクトを簡単に
特定できない場合には、PL/SQLプロシージャ"oerr12700"(R8.1以降で使用可能)
を実行することで ORA-600[12700]の引数から有効な情報を引き出すことができ
ます。

* プロシージャ"oerr12700"のPL/SQLソースコードは本文書の最後に添付します

出力例:

   SQL> execute oerr12700( 2989,16777219,4294941081 )
   ORA-600 [12700] [2989],[16777219],[4294941081]
   --------------------------------------------------
   there is an index pointing to a row in SCOTT.T12700
   row is slot 4294941081 in file 4 block 3
   one index entry is pointing to ROWID='AAAAutAAEAAAAADJmZ'
   --------------------------------------------------
   You may want to check the integrity of SCOTT.T12700
   executing :
   dbv file=<file_name> blocksize=<db_block_size> start=3 end=3
   --------------------------------------------------
   IF dbv does not show any corruption, you can try to
   find the corrupted indexes using the queries proposed
   by the procedure oerr12700diag(2989,16777219,4294941081)
   -------------------------------------------------------

これはある索引がSCOTT.T12700には存在しない行を指していることを意味しま
す。その行は file=4, block=3, slot=4294941081 であると出力されます。
索引エントリは ROWID 'AAAAutAAEAAAAADJmZ' を指し示しています。


 2.1 データ・オブジェクトが壊れているかどうかのチェック
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  ANALYZE TABLE <表名> VALIDATE STRUCTURE;

の実行によりデータ・オブジェクトが壊れてしまっているのか、そうでないの
かのチェックを行います。

このSQL文がエラーを返さないのであれば、データ・オブジェクトは正常であり、
索引が破損している可能性があります。

* ANALYZE TALBE 文はその実行中にその表の排他ロックを獲得します

表の排他ロックを保持したくないのであれば、上記のプロシージャ"oerr12700"
の出力例にあるdbvユーティリティの実行コマンドによりチェックを行って下
さい。

dbv file=<ファイル名> blocksize=<db_block_size> start=<block> end=<block>

* <block>はプロシージャ"oerr12700"を実行すると表示されます

dbvがエラーを返さない場合や、"Pages Failing"が 0 の場合、データ破損で
はなく索引破損の可能性があります。


-3- 索引自体が壊れてしまっている可能性はあるか確認
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ここでは表データ破損ではないケース、つまり索引破損の問題について確認し
ます。

-2-の項で例に挙げられている表には幾つかの索引が作成されている場合があり
ます。

索引が破損していないかどうかを確認する1つの方法として

 ANALYZE TABLE <表名> VALIDATE STRUCTURE CASCACE;

を実行し、エラーが発生した場合にはそのトレースファイルを確認するという
ものがあります。

先ほどと同様に ANALYZE TABLE ... 文を実行すると表の排他ロックが獲得され
ます。ロックを獲得せずに索引をチェックする場合にはプロシージャ"oerr12700diag"
を使用します。

* プロシージャ"oerr12700diag"のPL/SQLソースコードは本文書の最後に添付します

プロシージャ"oerr12700diag"は問題となるROWIDを指す索引エントリを含んで
いる索引を探し出すSQL文をリストします。

出力例:

   SQL> execute oerr12700diag(2989,16777219,4294941081)
   --------------------------------------------------
   IF dbv did not show any corruption, you can try to
   find the corrupted indexes using following queries:
   -------------------------------------------------------
   If a query returns "no rows selected" index is sane
   If a query returns AAAAutAAEAAAAADJmZ index is corrupted
   ..................................................
   .
   To test  SCOTT.T12700 indexes
   .
   .
   To test  INDEX I1T12700 you run :
   .
   select rowid "I1T12700 corrupted!" from
   (SELECT /*+ INDEX_FFS(T12700,I1T12700) */ 
   N,rowid from SCOTT.T12700 where N=N)
   where rowid='AAAAutAAEAAAAADJmZ';
   
   
   .
   To test  INDEX IT12700 you run :
   .
   select rowid "IT12700 corrupted!" from
   (SELECT /*+ INDEX_FFS(T12700,IT12700) */
   A,rowid from SCOTT.T12700 where A=A)
   where rowid='AAAAutAAEAAAAADJmZ';
   .

上記2つのSQL文の実行結果は以下の通りです。

SQL> select rowid "I1T12700 corrupted!" from
  2  (select /*+ INDEX_FFS(T12700,I1T12700)
  3  */ N,rowid from SCOTT.T12700 where N=N)
  4* where rowid='AAAAutAAEAAAAADJmZ'

no rows selected

上記の結果から索引I1T12700には、存在していない行を指している索引エント
リが存在しない、つまり、この索引は問題ないということが判ります。

SQL> r
  1  select rowid "IT12700 corrupted!" from
  2  (select /*+ INDEX_FFS(T12700,IT12700) */
  3  A,rowid from SCOTT.T12700 where A=A)
  4* where rowid='AAAAutAAEAAAAADJmZ'

IT12700 corrupted!
------------------
AAAAutAAEAAAAADJmZ

上記の結果から索引IT12700はROWID='AAAAutAAEAAAAADJmZ'で識別される、
SCOTT.T12700表に対する索引エントリが存在しており、この行はSCOTT.T12700
に存在していないということが判ります。つまり、索引IT12700は破損している
ということを表しています。

このことを二重にチェックするのであれば以下のSQL文を実行し、その表に
プロシージャ"oerr12700"で示されたROWIDが存在しないことを確認します。

 SQL> select * /*+ full (表名) */ from 表名
   2  where rowid='AAAAutAAEAAAAADJmZ'

注意:
~~~~~
ORA-600[12700]発生後のINSERT文でこの"BAD"ROWIDが使用された場合には、
確認SQL文にて複数行が返される場合があります。

例えば以下のような状況です:

  - 索引スロット3が存在しない行を指し示している状況
    
    ここでSELECT文を実行するとORA-600[12700]が返され索引の破損を検知
  
  - そのまま対処せずにINSERT文を実行したため、新しい行によりこの索引ス
    ロット3が使用される状況
    
    この場合SELECT文は失敗しませんが、UPDATE文はORA-600[13013]で失敗し
    ます。この破損した索引には、同じROWIDが2つの行を指すエントリが含ま
    れます。正常な索引では、1つの索引エントリは1つのROWIDを指します。


-4- 索引が壊れていた場合、その壊れた索引を再作成することは可能か
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

さらに詳細な調査を行う必要がある場合には、破損した索引を削除する前に
-7-で紹介する資料を取得しておく必要があります。

破損した索引を削除する場合、索引の依存性や削除した後の対処について事前
に準備しておく必要があります。

例えば破損した索引がプライマリ・キー制約や一意制約、参照整合性制約など
に使用されているのであれば、その索引を削除する前に制約を削除する必要が
あります。


-5- データが壊れていた場合にはDocument 28814.1を参照
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

-6- データも索引も壊れていない場合には読み取り一貫性の問題である可能性があります
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

読み取り一貫性の問題である場合

SQL*Plusを使用して現象を再現させることが可能であれば、再現テストの実行
前に イベント 10226 を設定して下さい。


SQL> alter session set events '10226 trace name context forever, level 10';
SQL> 現象を再現させることができるSQL文を実行

その他のアプリケーション(FormsやPro*Cなど)の実行で現象を再現させること
が可能ならば、現象の再現テスト実施前にアプリケーションに イベント 10226
の設定を組み込んで実施して下さい。

イベント 10226 の設定をアプリケーションに組み込めないのであればデータ
ベースを停止し、初期化パラメータ・ファイルに以下のエントリを書き加えて
下さい。

  event="10226 trace name context forever, level 10"

その後、データベースを起動し現象を再現させます。再現テストが終了したら
データベースを停止し、初期化パラメータ・ファイルからイベントの記述を削
除し、再度データベースを起動します。

どちらの方法を選択された場合でも、再現テスト実施時のalert_<SID>.log
および、user_dump_dest、background_dump_destで指定したディレクトリに出
力されるトレースファイルを全てまとめて弊社サポート・センターまで送付下
さい。

* イベント 10226 とはCRブロックの操作をトレースファイルにロギングする
  ためのイベントです

-7- データや索引が壊れており、さらなる分析が必要な場合
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

データブロック破壊や索引ブロック破壊が発生するケースでさらなる調査が
必要となる場合(頻繁に発生するような場合)には、以下の点についてご確認
頂き、弊社サポート・センターまで連絡下さい。

破損の原因を追及するために有効な運用環境

 - データベースがアーカイブ・ログ・モードで運用されていること
 - 破損したデータブロックや索引ブロックに関するREDOダンプを抽出可能な
   アーカイブREDOログファイルが存在すること

このような運用をされている場合には原因の究明に有効な資料を得られること
があります。

プロシージャ"oerr12700" と "oerr12700diag" のPL/SQL ソースコード
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

注意:これらのプロシージャはSYSユーザで作成し、使用します。
     これら2つのプロシージャは $ORACLE_HOME/rdbms/admin/dbmsutil.sql 
     によって作成されるDBMS_UTILITYとDBMS_ROWIDの2つのパッケージを使用
     しています。

************************* oerr12700 のソース *************************
CREATE OR REPLACE PROCEDURE oerr12700( a number , b number, c number) IS
  un varchar2(99);
  tn varchar2(99);
  trowid varchar2(99);
  ind_name varchar2(99);
  ind_col varchar2(99);
  nfile number;
  nblock number;
  nrow number;
  fname VARCHAR2(513) ;
  dbs number ;
  dbs_x varchar2(129);
  x number;

BEGIN

  x:= DBMS_UTILITY.GET_PARAMETER_VALUE('db_block_size',dbs,dbs_x);


  nfile:=DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(b);
  SELECT file_name
    INTO fname
    FROM dba_data_files
   WHERE relative_fno = nfile ;


  nblock:=DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(b);
  SELECT name,dba_users.username
    INTO tn,un
    FROM obj$,dba_users
   WHERE dataobj#=a
     AND dba_users.user_id=obj$.owner# ;

  trowid:= DBMS_ROWID.ROWID_CREATE(1,a,nfile,nblock,c);

  DBMS_OUTPUT.PUT_LINE(' ORA-600 [12700] ['||a||'],['||b||'],['||c||']');
  DBMS_OUTPUT.PUT_LINE('--------------------------------------------------');
  DBMS_OUTPUT.PUT_LINE('there is an index pointing to a row in '||un||'.'||tn);
  DBMS_OUTPUT.PUT_LINE('row is slot '||c||' in file '||nfile||' block '||nblock);
  DBMS_OUTPUT.PUT_LINE('one index entry is pointing to ROWID='''|| trowid||'''');
  DBMS_OUTPUT.PUT_LINE('--------------------------------------------------');
  DBMS_OUTPUT.PUT_LINE('You may want to check the integrity of '||un||'.'||tn);
  DBMS_OUTPUT.PUT_LINE('executing :');
  DBMS_OUTPUT.PUT_LINE('dbv file='||fname||' blocksize='||dbs||' start='|| nblock||' end='||nblock);
  DBMS_OUTPUT.PUT_LINE('--------------------------------------------------');
  
  --
  DBMS_OUTPUT.PUT_LINE('IF dbv does not show any corruption, you can try to');
  DBMS_OUTPUT.PUT_LINE('find the corrupted indexes using the queries proposed');
  DBMS_OUTPUT.PUT_LINE('by the procedure oerr12700diag('||a||','||b||','||c||')');
  DBMS_OUTPUT.PUT_LINE('-------------------------------------------------------');
END;
/

********************** oerr12700diag のソース ************************
CREATE OR REPLACE PROCEDURE oerr12700diag( a number , b number, c number) IS
  un varchar2(99);
  tn varchar2(99);
  trowid varchar2(99);
  ind_name varchar2(99);
  ind_col varchar2(99);
  nfile number;
  nblock number;
  nrow number;
  CURSOR pindexes(towner varchar2, tname varchar2) is
    SELECT C.INDEX_NAME,COLUMN_NAME
      FROM dba_ind_columns C, dba_indexes I
     WHERE c.INDEX_NAME=i.INDEX_NAME
       AND I.INDEX_TYPE <> 'DOMAIN'
       AND C.TABLE_OWNER=towner
       AND C.TABLE_NAME=tname
       AND C.COLUMN_POSITION=1 ;

  rpindexes pindexes%ROWTYPE;

BEGIN
  nfile:=DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(b);
  nblock:=DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(b);
  SELECT name,dba_users.username
    INTO tn,un
    FROM obj$,dba_users
   WHERE dataobj#=a
     AND dba_users.user_id=obj$.owner# ;

  trowid:= DBMS_ROWID.ROWID_CREATE(1,a,nfile,nblock,c);

  DBMS_OUTPUT.PUT_LINE('--------------------------------------------------');
  DBMS_OUTPUT.PUT_LINE('IF dbv did not show any corruption, you can try to');
  DBMS_OUTPUT.PUT_LINE('find the corrupted indexes using following queries:');
  DBMS_OUTPUT.PUT_LINE('-------------------------------------------------------');
  DBMS_OUTPUT.PUT_LINE('If a query returns "no rows selected" index is sane');
  DBMS_OUTPUT.PUT_LINE('If a query returns '||trowid||' index is corrupted');
  DBMS_OUTPUT.PUT_LINE('..................................................');


  DBMS_OUTPUT.PUT_LINE('.');
  DBMS_OUTPUT.PUT_LINE('To test  '||un||'.'||tn||' indexes ') ;
  DBMS_OUTPUT.PUT_LINE('.');
  FOR rpindexes IN pindexes(un,tn) LOOP
    DBMS_OUTPUT.PUT_LINE('.');
    DBMS_OUTPUT.PUT_LINE('To test  INDEX '||rpindexes.INDEX_NAME||' you run :' );
    DBMS_OUTPUT.PUT_LINE('.');
    DBMS_OUTPUT.PUT_LINE('select rowid "'||rpindexes.INDEX_NAME||' corrupted!" from ');
    DBMS_OUTPUT.PUT_LINE('(SELECT /*+ INDEX_FFS('||tn||','||rpindexes.INDEX_NAME||') */ ');
    DBMS_OUTPUT.PUT_LINE(rpindexes.COLUMN_NAME||',rowid from '||
                         un||'.'||tn||' where '||
                         rpindexes.COLUMN_NAME||'='||rpindexes.COLUMN_NAME||') ' );
    DBMS_OUTPUT.PUT_LINE( 'where rowid='''||trowid||''';'||' ');
  END LOOP ;
END;
/
**********************************************************************