【ITニュース解説】ORA-01555 Snapshot Too Old: Reproduce It, Then Make It Impossible
2026年10月01日に「Dev.to」が公開したITニュース「ORA-01555 Snapshot Too Old: Reproduce It, Then Make It Impossible」について初心者にもわかりやすく解説しています。
ITニュース概要
ORA-01555エラーは、長時間クエリが過去時点のデータ参照に必要なUNDO情報が、大量のデータ変更で上書きされ失われることで発生する。これはクエリ自体の問題ではなく、UNDO設定(テーブルスペースサイズ、保持期間、RETENTION GUARANTEE)の不備が原因だ。適切なUNDOサイジングと`RETENTION GUARANTEE`設定で解決できる。
ITニュース解説
Oracleデータベースを使用していると、まれに「ORA-01555: snapshot too old」(スナップショットが古すぎます)というエラーに遭遇することがある。これは、特に長時間実行されるレポートやデータ連携処理などが途中で突然停止してしまう現象だ。通常、クエリの内容を変えていないのに、なぜか失敗したり、時間を変えて再実行すると成功したりするため、原因が掴みにくい。多くの人は、クエリ自体に問題があるのではないかと考えがちだが、このエラーの本質はまったく異なる。ORA-01555は、クエリが「間違っている」のではなく、むしろOracleがデータの一貫性を「正しく守ろうとしている」ために発生するエラーなのである。
Oracleデータベースは、「読込一貫性(Read Consistency)」という非常に重要な機能を提供する。これは、どんなに時間がかかるクエリであっても、クエリが開始した瞬間のデータ状態を正確に反映した結果を返す、というものだ。例えば、あなたが長いレポートを実行し始めたとき、そのレポートは、クエリ開始時にデータベースにあったデータだけを使って結果を生成する。たとえレポートの実行中に他のユーザーがデータを変更・保存(コミット)したとしても、あなたのレポートはそれらの新しい変更を無視し、あくまで開始時点の「スナップショット」と呼ばれる一貫したデータ像を見続けるのだ。この読込一貫性を実現するために、Oracleは「undo(アンドゥ)」という仕組みを利用する。データが変更される際、Oracleはその変更前の状態(「ビフォーイメージ」と呼ぶ)を「undoテーブルスペース」という特別な領域に記録する。もし長時間実行中のクエリが、自分の開始後に変更されたデータブロックを読み込もうとした場合、Oracleはこのundoデータを使って、そのデータブロックをクエリ開始時点の状態に「再構築」し、一貫した情報を提供する。
では、なぜORA-01555が発生するのか。このエラーは、クエリが必要とする変更前のデータ(undo)が、もはやundoテーブルスペース内に存在しない場合に発生する。undoデータは永遠に保存されるわけではない。一定期間が過ぎると、そのスペースは新しいデータの変更(DML: Data Manipulation Language、つまりINSERT, UPDATE, DELETEなど)によって再利用されてしまう。もし、非常に長く実行されているクエリが、その実行中に他の大量のDML処理によって、自分が参照するはずだったundoデータが上書き・再利用されてしまったらどうなるか。Oracleは、クエリ開始時点の一貫したデータ状態を再構築できなくなる。この時、Oracleは部分的に新しいデータと古いデータが混在した「間違った」結果を返すことを拒否し、代わりに「ORA-01555」エラーを発してクエリを停止させるのだ。このエラーメッセージに「rollback segment too small」(ロールバックセグメントが小さすぎる)とあるが、これは古い表現であり、実際には「必要なundoデータが再利用されてしまった」というメッセージだと理解するべきである。
ORA-01555が発生するには、通常以下の三つの状況が同時に揃うことが多い。一つ目は「長時間実行されるクエリ」で、レポートやデータ連携処理など、実行に時間がかかるクエリは、過去のundoデータを参照する可能性が高まる。二つ目は「大量の同時DML」で、データベース上で頻繁にデータの変更操作が行われ、大量のundoデータが生成される状況では、undoテーブルスペースのデータが速いペースで消費され、再利用される。三つ目は「undoデータが不足する構成」で、これにはundoテーブルスペースのサイズ不足、undoデータを保持しておく期間を示す「undo_retention」の設定不足、そして「RETENTION GUARANTEE」が設定されていないことが含まれる。特に重要なのは最後の点で、デフォルト設定(RETENTION NOGUARANTEE)では、たとえundo_retentionで設定された期間内のundoデータであっても、undoテーブルスペースの容量が逼迫すると、OracleはDML処理の失敗を避けるために、そのundoデータを上書きしてしまう。つまり、undo_retentionは「目標」であって「保証」ではないのだ。
ORA-01555を根本的に解決するには、クエリではなく、undoの構成を見直す必要がある。具体的には以下の三つの対策を講じる。第一に、最も長く実行されるクエリの実行時間よりも、「undo_retention」の値を十分に長く設定する。例えば、最長のレポートが90分かかるなら、undo_retentionは90分(5400秒)以上に設定すべきだ。第二に、undoテーブルスペースに「RETENTION GUARANTEE」(リテンション・ギャランティ)を設定する。これにより、undo_retentionで指定された期間内のundoデータは、いかなる場合でも上書きされなくなる。Oracleは、DML処理のために領域が必要になったとしても、期限内のundoデータを削除する代わりに、DML処理をエラー(ORA-30036: undoテーブルスペースにセグメントを拡張できません)で失敗させるようになる。これは、読込一貫性を「保証」するための重要な設定であり、通常、レポートやデータ連携処理のウィンドウにおいては、書き込み処理が一時的に失敗するよりも、読み取り処理が一貫性のないデータを返す方が問題となるため、正しい選択だと言える。第三に、undoテーブルスペースの適切なサイジングを行う。RETENTION GUARANTEEを有効にした場合、undoテーブルスペースが不足するとDMLが失敗するようになるため、ワークロード(最長クエリの実行中に発生するDMLの量)に対して十分なサイズを確保する必要がある。自動拡張(AUTOEXTEND)を有効にするか、V$UNDOSTATビューなどでundoデータの生成量を測定し、余裕を持ったサイズに設定することが重要だ。
ORA-01555はほとんどの場合、クエリのSQL文の誤りではなく、undo構成の問題が原因であるため、クエリを書き換えても解決しないことが多い。ただし例外的に、「fetch-across-commitアンチパターン」という、カーソルを開いてデータをフェッチし、そのループ内で同じデータを更新し、コミットを繰り返すコーディングパターンでは、自分自身がコミットするたびにundoデータが再利用可能になり、自分のカーソルが後で必要とするundoを破壊してしまうため、コードの修正が必要となる。また、undoはデータの変更前の状態(読込一貫性やロールバック用)であり、redoは全ての変更操作のログ(障害回復用)であるため、これらを混同しないことも重要だ。
最後に、ORA-01555が発生してもデータが失われることはないという点も知っておくべきだ。Oracleは一貫性のないデータを返すことを拒否しているだけであり、データベース内のコミットされたデータは完全に整合性が保たれている。
ORA-01555は、Oracleデータベースが提供する「読込一貫性」という強力な機能の裏返しとして発生するエラーだ。このエラーに遭遇した際は、まずundoテーブルスペースのサイズ、undo_retentionの設定、そして特に「RETENTION GUARANTEE」の有効化といったundo構成の問題を疑うべきである。適切な設定を施し、V$UNDOSTATなどのツールを使ってデータベースの活動状況を測定することで、この厄介なエラーを効果的に「不可能にする」ことができる。これは単なる回避策ではなく、データベースの健全性を保ち、信頼性の高いデータ運用を実現するための本質的な対策なのである。