なんとなくを腑に落とすシリーズ ~第1回 UNDO(前編)



坂本 昌典

オラクル事業部
システムインテグレーション担当

坂本 昌典

はじめに

はじめまして。NTTデータ先端技術の坂本です。

現在、Oracle Exadata Database Machineを中心にOracle Engineered Systemsの導入、維持などを行っていますが、Oracle Databaseにつきましては、バージョン7.2からのかなり長い付き合いになります。バージョン7.2といいますと、データベースの作成はコマンドベースでした。新人の頃、Trial & Errorで何度やり直したか分かりませんが、助けを得ながらも最終的にデータベースを作成・起動できた時、とにかく厳しい先輩に褒められ嬉しかったのを覚えています。

オプティマイザもルールベースがまだ主流であったり(ルールベースってご存知ですか?)、Real Application Clusters(RAC)はまだ存在せず、クラスタとしては前身であるOracle Parallel Server(OPS)が活躍していたりと思い返せばいろいろ懐かしいのですが、懐古趣味に拍車が掛かりそうですのでこの辺りで止めておきます。

さて、長くOracle Databaseを触ってきて、基本的なことはそれなりに理解してきたつもりではおりますが、現場で対応していると、理解が「なんとなく」だったのだなという事柄が正直結構あります。そこで、振り返りも兼ねて「なんとなくを腑に落とす」シリーズとして今後取り上げていければと思っています。今回はUNDOについて紹介したいと思います。

※ ルールベースとは、問い合わせ時にデータへのアクセスパスを決定するオプティマイザの動作において、データの統計に基づかず、例えば「索引によるアクセスが早いであろう」、「全表を走査するのは遅いであろう」といった一般的な考えに基づき決められたランクの中から、取り得る最も高いランクのアクセスパスを採用するモードのことです。Oracle Database 10g以降からはサポートされなくなっております。

UNDOとは

Oracle Databaseを使用したことのある方であれば、UNDOについてご存じでない方はいないと思います。UNDOと聞いてすぐに思い起こされるのが「ロールバックする際に必要なもの」とか、あるいは開発や運用に長く携わっている方ですと、脊髄反射でORA-01555が出てくるかもしれません。

UNDOとは「元に戻す」という意味で、例えばviエディタの元に戻すための『u』キーはUNDOの頭文字です。Oracle Databaseでは元に戻す操作自体はUNDOとはいわずロールバックといいますが、その元に戻すために必要なレコードを総称してUNDOといいます。Oracle Databaseではトランザクションにて更新が発生すると、その更新前データをUNDOセグメントと呼ばれるオブジェクトに格納します。UNDOセグメントはオブジェクトの一種ですが、表などとは違い特定のスキーマに対応付けられない特殊なオブジェクトで、非スキーマオブジェクトの1つです。そのUNDOセグメントに格納された更新前データをUNDOデータと呼びます。

なお、UNDOセグメントの管理方法としては自動UNDO管理と手動UNDO管理の2種類が存在します。自動UNDO管理の場合は、専用のUNDO表領域を用意し、初期化パラメータUNDO_MANAGEMENTを”AUTO”に設定し、UNDO_TABLESPACEにそのUNDO表領域名を設定することで自動UNDO管理にてデータベースが運用されることとなります。UNDOセグメントも手動で事前に作成しておく必要はありません。

現在、ほとんどの環境が自動UNDO管理で運用されているかと思いますので、以降、自動UNDO管理を前提に説明していきます。

UNDOデータはなぜ必要なのか

まずは、必要とするケースにどのようなものがあるのかまとめます。

① トランザクションのロールバックをする時

UNDOの言葉の意味通り、ロールバックする際に使用されます。UNDOセグメントは更新前のデータが保存されているため、トランザクションをコミットせずロールバックする場合はこのデータを使ってレコードを元に戻します。この用途は特にひねりもなく、理解しやすいかと思います。

② 他のトランザクションで更新中のデータを読み取ろうとする場合(読取り一貫性の確保)

これもOracle Databaseでは重要なUNDOデータの利用ケースとなりますが、他のトランザクションが現在更新中のデータを読み取ろうとする場合は、このUNDOデータをもとに更新前データを読み取ることによって、読取り一貫性を保つことが可能となります。

読取り一貫性について簡単な例を一つ説明しますと、例えば大きなテーブルAをトランザクションT1が全件検索をしている途中で、別のトランザクションT2がテーブルAに1件更新を掛けコミットしたとします。トランザクションT2が更新した際に発生したUNDOデータは、コミット済みですのでロールバック用という観点ではもう不要です。しかし、トランザクションT1は読取り一貫性の観点から、T2が更新したレコードについてその更新前レコードを読み取らなければいけません。なぜならば、トランザクションT1は読取りを始めた時点におけるテーブルAのレコードを問い合わせているにもかかわらず、一部分だけその後に更新されたレコードを読み取っていては、データの一貫性がないからです。ですので、コミット後であってもUNDOデータが残っていることが必要となります。

Oracle Databaseではトランザクションレベルでの読取り一貫性(トランザクション開始時点におけるコミット済みデータでの一貫性)と、文レベルでの読取り一貫性(問い合わせ開始時点におけるコミット済みデータでの一貫性)があり、どちらなのかによって必要とするUNDOデータの生成時期は異なりますが、いずれにしても、対象データが更新中に読み取りを行う場合、UNDOセグメントに取り置きしてあるUNDOデータを元に一度更新前の状態に復元して読み取ることによって読取り一貫性を確保します。自身のトランザクションのロールバックの時だけのためでなく、他のトランザクションの問い合わせのためにも、UNDOデータは状況に応じて必要となってくるという訳です。

なお、問い合わせ時に発生することがある有名なエラー「ORA-01555:スナップショットが古すぎます」はこの読取り一貫性のために必要となったUNDOデータが既に上書きされていて読み取ることが出来なかった場合に発生します。

(補足)Oracle Databaseでは、コミット時のパフォーマンス向上のため、データブロックがコミット済みであることを記録する『ブロッククリーンアウト』と呼ばれる処理が必ずしもコミット時に行われず、次に該当ブロックがアクセスされた際に、UNDOセグメント上の情報を使って行われることがあります。その為、問い合わせ開始時点で対象レコードがすべてコミット済み、つまり本来であればUNDOデータを必要としない状況であってもORA-01555が発生することがあります。

上書きされる理由を一言で言いますと、「UNDO表領域がいっぱいでそのままでは新しく発生するUNDOデータを格納できないため」です。

まず、自動UNDO管理モードでは、UNDOデータをコミット後に少なくともどれだけ保持しておくべきかを示す「保持期間」というものが設定され、この値はバージョン9.2までは初期化パラメータUNDO_RETENTIONで指定し、バージョン10.1以降では内部的に自動で設定されるTUNED_UNDORETENTIONの値が採用されます(この場合初期化パラメータUNDO_RETENTIONはTUNED_UNDORETENTIONの下限値として働きます)。

※ UNDO_RETENTION/TUNED_UNDORETENTIONについては後編で詳しく説明します。

UNDOデータの上書きが発生するケースとしては、まずUNDOデータ生成時にUNDOセグメントが一杯の場合、現状のUNDO表領域の空き領域にエクステントを追加しようとしますが、追加できる領域がない場合は、保持期間の過ぎたUNDOデータがあればそのUNDOデータを上書きしようとします。さらにその様なUNDOデータもない場合は、表領域自体を拡張してエクステントを追加しようとしますが、表領域サイズが固定であったり容量不足や最大サイズの指定により表領域の拡張ができなかったりする場合は、仕方なく保持期間が過ぎていないUNDOデータを上書きすることによって、新しく発生するUNDOデータを保存します。

もし保持期間の過ぎていないUNDOデータの上書きの発生を禁止したい場合は、UNDO表領域のRETENTIONオプションをGUARANTEEに設定します(禁止しない場合はNOGUARANTEEに設定します)。その場合、初期化パラメータUNDO_RETENTIONの範囲内において、保持期間の過ぎていないUNDOデータを守ることはできますが、上書きさえすれば継続できるような更新処理でも「ORA-30036:UNDO表領域内でセグメントを拡張できません」が発生し更新処理がエラーとなります。

RETENTIONオプションをどちらに設定するかは、そのような保持期間が過ぎていないUNDOデータの上書きを迫られる状態において、今目の前にある更新処理を進めることを優先するか、後々の読取り一貫性のためにUNDOデータを参照しようとした際にORA-1555が発生しないようにすることを優先するか、という業務要件次第になります。通常は必ず必要となるかはわからないUNDOデータを守るより、更新処理を優先するケースが一般的かと思います。Oracle Databaseのデフォルトも上書きを禁止しないNOGUARANTEEとなっています。

③ リカバリをする時

リカバリにUNDOデータが必要となると聞いて一瞬不思議な気持ちになりますが、これは『トランザクションリカバリ』と呼ばれるもので、例えば、トランザクション中にインスタンスプロセスがクラッシュした場合、データ自身は更新されてしまっているため、リカバリ時において、UNDOデータを利用してコミットされていない更新を取り消します。基本的には①と同様で、これもロールバックです。

④ フラッシュバック機能を利用する時

フラッシュバッククエリやフラッシュバックテーブルなど、リカバリ操作を実施しなくても過去の時点でのデータを問い合わせたり、テーブルの状態を過去の時点に戻したりできますが、これらの機能にもUNDOデータが使われます。内部的に行われている動きとしては、基本的に①のロールバックであったり、②の読取り一貫性の機能であったりします。フラッシュバック機能につきましては、このテーマで1つのコラムになりえるボリュームですので、また機会をみて紹介させていただきます。

今回のまとめ

今回は「なんとなくを腑に落とすシリーズ 〜第1回 UNDO」の前編として、UNDOとは何か、そしてUNDOデータはどのようなケースで必要となるかについて見ていきました。次回コラムでは後編としてUNDOを扱う上で押さえておきたい点につきまして紹介したいと思います。

本コラムの内容やオラクル製品に関するご質問等については、画面右側の「お問い合わせ」ボタンまたはオラクル事業部の窓口まで直接お問い合わせください。



なんとなくを腑に落とすシリーズ ~第1回 UNDO(前編)