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

Oracle ORA-1555発生時の対処法

Oracle ORA-1555発生時の対処法

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

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

 

[Problem]

ORA-1555「スナップショットが古すぎます」発生時の対処法

 

[Action]

ORA-1555 が発生する原因及び回避策につきまして列挙します。

 

 

【読取り一貫性】

 

  オラクルでは各SQL文ごとに読取り一貫性が保証されています。

  つまり、ユーザがSQL文を発行した時点のデータを必ず返すということを意味します。

  この為、検索を発行したユーザは、検索中に他のユーザによって変更されたデータを

  見ることはありません。

 

  オラクルはシステム変更番号System Change Numbers (SCN)によって、データベース

  の状態を一意に識別します。SCNはコミットが発生するごとに増加しますので、時間

  が経つにつれて必ず大きくなります。このSCNを使って読取り一貫性を実現していま

  す。

 

  読取り一貫性はロールバックセグメントによって実現されています。

  トランザクションが何等かの変更をする度にロールバックセグメントには、そのビ

  フォアイメージが格納されます。データブロックのヘッダー部分には、どこのロール

  バックセグメントが使用されたかが記されます。また、同様に最後にコミットされた

  SCNの値をデータブロックは保持しています。

 

 

【ORA-1555が発生する原因】

 

1.  RBSの大きさに対して、データベースへの変更が多い

 

    データベースを更新するトランザクションが多く、頻繁にコミットが発せられる場

    合には、RBS上のUNDOブロックが再利用される(=上書きされる)確率が高くなりま

    す。こういった状態のシステムに対して長い検索を行なうと、ビフォアイメージの

    取得ができないことが多々発生します。

 

【処置】

    RBSの数を増やしたり、各々のRBSを大きくします。

 

 

2.  OPTIMALの値が小さい

 

    1.は後続するトランザクションが既にコミットされたトランザクションのUNDO情報

    を上書きしてビフォアイメージが失われるケースでしたが、OPTIMALを設定してい

    る場合、エクステントの開放によってビフォアイメージが失われることがありえま

    す。したがって、トランザクションの長さに対してOPTIMALの値が小さい場合には

    ORA-1555が多発することが考えられます。

 

【処置】

    OPTIMALの設定値を大きくする。

 

 

3.  コミット前からの検索

 

    カーソルをオープンし、フェッチ -> 更新 -> コミット を繰返し同じ表に対して

    行なう場合に ORA-1555 はよく発生します。

 

              SCN =10         OPEN CURSOR 

                              FETCH TABLE A

                              UPDATE TABLE A

                  =20         COMMIT

                              ...

                              FETCH TABLE A

                              UPDATE TABLE A

                  =50         COMMIT

                              ...

                              FETCH TABLE A

                              UPDATE TABLE A

                  =100        COMMIT

 

    例えば、カーソルがSCN=10でオープンされたとします。検索開始時間がSCN=10で

    記録されるので、このカーソルによってフェッチされたデータは全てSCN=10以前

    のものでなければなりません。

 

    次に、ユーザのプログラム側では、x行のレコードをフェッチし、更新・コミット

    を行ないます。

    ここで、一回目のコミットがSCN=20で発生したとします。このSCN=20でコミット

    されたブロックを、後のフェッチでアクセスしたときにはSCN=10の情報を取得する

    ためにRBSを見にいきます。

 

    フェッチ・更新・コミットを繰り返すことによって、SCN=10の情報を持っているRBS

    のUNDOブロックがいずれ再利用される可能性があります。再利用されてしまった後

    のフェッチでビフォアイメージが取得できないと、ORA-1555が発生します。

 

【処置】

    コミットの回数を減らすことにより、SELECTが要求しているSCN以前のビフォアイ

    メージが上書きされる可能性が低くなります。したがって、ORA-1555が出る確率は

    これで下がります。

 

 

4.  コミット前からの検索と遅延ブロッククリーンアウト

 

    上記3.はあるひとつの表に対してフェッチしながらコミットする例ですが、遅延

    ブロッククリーンアウトがCURSOR OPEN前に行われていなかった場合、話は更に

    複雑になります。

 

    トランザクションのコミット情報はデータブロックとRBSの2箇所で持ちます。

    データやインデックスを更新した後にコミットを行なった時点では、RBSの方に

    だけコミット情報が記録され、データブロックの方には記録されていません。

    次のトランザクションに於いて、該当のデータブロックが検索された際に「ブロッ

    ククリーンアウト」が発生します。この時点でデータブロックのトランザクション

    情報が更新され、データブロックにもコミットされたことを示すフラグがたちます。

    この動作を「遅延ブロッククリーンアウト」と呼びます。

 

    以下の様な状態を想定します。

 

    表AとBの二つのテーブルがあります。二つの表に対して 表Aをフェッチ -> 表Bを

    更新 -> コミット の一連のトランザクションが行われます。(この中では表Aに対

    し一切変更がかかっていないことに注意してください。)

 

              SCN =10         OPEN CURSOR 

                              FETCH TABLE A

                              UPDATE TABLE B

                  =20         COMMIT 

                              ...

                              FETCH TABLE A

                              UPDATE TABLE B

                  =50         COMMIT

                              ...

                              FETCH TABLE A

                              UPDATE TABLE B

                  =100        COMMIT

 

       1. 表Aに対するUPDATE/COMMITが発生し、コミットの記録はRBSにだけ残って

          おり、データブロックにはコミットされたことは記録されておりません。

          この時点で上記の作業を開始したとします。

       2. 表Aに対するSELECT文のCURSORをOPEN。(この例ではCURSORは SCN=10 の

          状態のデータを検索しようとします。)

       3. 表Bに対するUPDATE/COMMITがたくさん発生することで、RBS HEADER内の

          トランザクション・テーブルにあるスロットが使われていきます。

       4. 使われていく間にスロットは再利用され、1.で記録された情報が上書きさ

          れます。(すでにコミットされているため)

       5. ここで、まだ表Aに対するSELECTが続行中で、コミットされていない状態

          のデータブロックを見つけた場合、RBSを確認しにいきます。

       6. 5.の要求を受けて見に行ったRBSのトランザクション・テーブルには、上書

          きされてしまったためにそのトランザクションの記録がないことがありえ

          ます。

       7. SELECTが要求するSCNとRBS上のCOMMIT SCNを比べ、SELECTが要求するSCN

          (この例では 10)より大きなSCNによって上書きされているとき(例えば

          この例における表Bに対する SCN>10 のUPDATE/COMMIT)、ORA-1555 が発生

          します。

 

          (SELECTが要求するSCNより小さなSCNによってRBS上の情報が上書きされて

          いる場合には、遅延ブロッククリーンアウトを行ないつつ検索を続行しま

          す。例えば、上記の例において SELECT が ORA-1555 で失敗した後、再度

          SELECTのCURSORをOPENした場合、そのSELECTが要求するSCNは100以上に

          なっていますので、表Bに対する SCN<=100 の更新が原因で再度 ORA-1555 

          が発生することはありません。)

 

    上記のように、表Bに対してのみコミットを行なっているにも関らず、表Aの検索で

    ORA-1555が発生することがあります。これは表Aのブロックに対して事前にブロック

    クリーンアウトが発生していないことが原因です。

 

【処置】

    エラー発生時には該当 SQL を再実行して運用を続けて下さい。

    ブロックがクリーンアウトされる前に、RBS HEADER内のトランザクション・テー

    ブルが再利用されてしまうことによってこの問題は発生しているので、RBSの数を

    増やすことを検討してください。あるいは、更新トランザクションを開始する前

    に表Aのコミット情報をデータブロックに反映するため、ブロッククリーンアウト

    を発生させて回避することもできます。

 

    1. TABLEのブロック・クリーンアウト

 

        SELECT /*+ FULL(表名) */ COUNT(*) FROM 表名;

 

    2. B-Tree索引のクリーンアウト

       検索しているTABLEにB-Tree索引が作成されていた場合、B-Tree索引の

       クリーンアウトも行って下さい。

 

        SELECT /*+ INDEX(表名 索引名) */ 索引列 FROM 表名

        WHERE 索引列 IS NOT NULL;

       

       ※索引が複数使用されている場合は2.の索引名を変更して、全索引に対し

         上記SQLを実行してください。ただし、索引列にNOT NULL制約が指定

         されている場合には、"索引列 IS NOT NULL"条件句は必要ありません。

 

       ※索引が複数のカラムに対して定義されている場合(複合索引)は、2.の

         索引列の個所にその複数カラムを全て指定して下さい。索引の先頭列が

         NOT NULLでない場合、WHERE句に "先頭列 IS NOT NULL" を条件指定

         して下さい。

 

 

 

5.  RBSが破壊されている

 

    問題のビフォアイメージが含まれているRBSが破壊されている場合には、ビフォア

    イメージを取得することはできません。

 

【処置】

    現在のトランザクションを続行することは無理ですので、RBSの回復を行なってか

    ら再度試行して下さい。

 

 

コメント:コミット前からの検索(3.と4.のケース)はANSIではサポートされていませ

          ん。検索を始めた後にコミットが発生する場合には、カーソルを一旦無効に

          し(クローズ)再度カーソルをオープンすることを、ANSIでは標準としてい

          ます。

          オラクルではコミット前からの検索を有効としていますが、これによって

          ORA-1555 が発生する恐れがあることを認識する必要があります。

 

 

[Workaround]

 

<ロールバックセグメントの作成にあたっての基本的な方針>

 

・RBSを配置するための専用の表領域を使用し、他のオブジェクトと分ける。

・全て同じエクステントサイズになるように、INITIAL , NEXTの値を同じに設定する。

・MAXEXTENTS は小さめに設定し、UNLIMITEDなどには設定しない。

・ROLLBACK SEGMENTを作成する時点で、そのサイズが事前に見積もった容量分に達する

  ように、STORAGE句で MINEXTENTSをきちんと設定する

 

 

また、バッチ処理等による大量のDML文を実行するなどの場合には、

あらかじめ大きめのRBSを作成しておき、

 

  SET TRANSACTION USE ROLLBACK SEGMENT <rollback_segment_name>;

 

で明示的に使用するRBSを割り当てることによって、無用のEXTENT拡張を起こさない

ようにして下さい。