現在地

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



坂本 昌典

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

坂本 昌典

はじめに

前回は、UNDOとは何か、そしてUNDOデータはどのようなケースで必要となるかについて見ていきました。後編となる今回は、UNDOを扱う上で押さえておきたい点につきまして紹介したいと思います。

押さえておきたい点

① TUNED_UNDORETENTIONの下限値となるUNDO_RETENTION初期化パラメータの値はUNDO表領域の自動拡張がONでないと有効に働かない

前回 でも簡単に触れましたが、バージョン10.1以降では、常に自動算出されたTUNED_UNDORETENTIONと呼ばれるコミット済みUNDOデータの保持期間の目標値をデータベース内部で持っています。詳細は公開されておりませんが、UNDO表領域の自動拡張が有効の場合は、データベースでのそれまでの最大クエリー時間をベースに見積もられた値が設定され、UNDO表領域の自動拡張が無効の場合は、UNDO表領域の使用率が一定の値を上回らないように見積もられた値が設定されます。この値はデータベースの稼働中常に更新され、既存のUNDOデータの上書きが必要となった場合の対象を選別する基準として利用されます。

現在のTUNED_UNDORETENTIONはV$UNDOSTATビューより確認できます。

-- ▼TUNED_UNDORETENTION の確認 -----
SQL>
select
to_char(BEGIN_TIME,'YYYYMMDD HH24:mi:ss'),
to_char(END_TIME,'YYYYMMDD HH24:mi:ss'),
TUNED_UNDORETENTION
from V$UNDOSTAT where BEGIN_TIME >= SYSDATE-1/24 order by 1;

TO_CHAR(BEGIN_TIM TO_CHAR(END_TIME, TUNED_UNDORETENTION
----------------- ----------------- -------------------
20180814 18:14:42 20180814 18:24:42               14400
20180814 18:24:42 20180814 18:34:42               14400
20180814 18:34:42 20180814 18:44:42               14400
20180814 18:44:42 20180814 18:54:42               14400
20180814 18:54:42 20180814 19:04:42               14400
20180814 19:04:42 20180814 19:07:24               14400  ★<==単位は秒

6行が選択されました。

V$UNDOSTATには10分間隔でインスタンス内でのUNDOに関連する統計が収集されますので、最新のレコードの値が現在の値となります。コミット済みのUNDOデータであっても必要となるケースがあるのは前回説明した通りですが、実行時間の長いトランザクション(あるいは問い合わせ)の裏で別のトランザクションにより更新が走るような業務では、この値が長めである必要があります。

そして、似たような名前でUNDO_RETENTIONという初期化パラメータがあります。

バージョン9.2までは、このUNDO_RETENTIONの値がそのまま保持期間の目標値となっていましたが、10g以降はTUNED_UNDORETENTIONに設定される値の下限値として働くようになりました。例えば、自動算出された値が2000でUNDO_RETENTIONの値が3000であれば、TUNED_UNDORETENTIONの値は3000に設定されます。業務設計上、確実に必要な保持期間の目標値が分かっている場合は、UNDO_RETENTION初期化パラメータにその値を設定することで一応の保証ラインとして指定することが可能となります。ただし、バージョン10.2以降で注意しておきたい点があります。それはUNDO表領域の自動拡張がONになっている場合のみUNDO_RETENTION初期化パラメータの値が下限値として使用されるという点です。自動拡張がOFFの場合はUNDO_RETENTIONの値は無視されますのでご注意ください。もしUNDO表領域の設計としてサイズを固定したい場合にUNDO_RETENTION初期化パラメータによる下限値設定も行いたい場合は、自動拡張はONとしつつデータファイルのMAXSIZE設定でキャップを掛けることで実現します。

なお、保持期間を過ぎていないUNDOデータの上書きが発生する場合は、なるべく新しいUNDOデータが上書きされないよう当然考慮されているようですが、「どうせ古いものから上書きするだろうから、UNDOセグメントが保持期間の過ぎていないUNDOデータで満たされてしまっても問題ないだろう」との考えのもと、むやみやたらにUNDO_RETENTIONの値を大きくすることは、不要な表領域の圧迫やデータファイルの拡張を招き、また、バッファキャッシュの浪費にもつながりますので、必要以上には大きくしないことをお勧めします。

② 行外のLOBに対するUNDOデータの保存ポリシーは通常のUNDOデータと異なる

行内に収まらず、別のLOBセグメントに保存されるLOBに対するUNDOデータについてはLOBセグメント内に格納され、これまで述べてきた保存ポリシーとは異なります。LOBのSTORAGE属性として、UNDOの保存ポリシーにPCTVERSIONかRETENTIONを選べますが、PCTVERSIONの場合は、LOBデータ領域におけるLOBのUNDOデータの保持スペースの割合を指定することとなり、その値の範囲内ではUNDOデータを保持しておきますが、それ以上になると上書きされることになります。RETENTIONの場合は、通常のUNDOデータの考え方とほぼ同じとなりますが、TUNED_UNDORETENTIONの値は採用されず、UNDO_RETENTION初期化パラメータの値がそのまま採用されます。

③ マルチテナント環境ではローカルUNDOモードでPDB間の影響を排除(Oracle Database 12c Release 2以降)

Oracle Database 12c Release 2以降からの機能となりますが、マルチテナント環境において、PDB毎にUNDO表領域を作成することが可能となりました。(Oracle Database 12c Release 1では、CDB全体としては1つのUNDO表領域を共有する共有UNDOモードのみでした)

ローカルUNDOモードにすることにより、万一各PDB間での一時的なUNDOの利用量のバーストがあった場合にもその影響を他のPDBに与えることが無くなります。また、PDBレベルでのフラッシュバックデータベースも可能となります。Oracleの推奨ということもありますが、昨今のストレージの大容量化により、UNDO表領域用の容量をPDBそれぞれに確保することは以前ほど難しくはなくなりましたので、マルチテナント環境では積極的に利用していきたい機能の1つになります。

④ 開発初期でのUNDO表領域のサイズ見積りについて

まず、UNDO表領域の見積もりはUNDOアドバイザの利用が推奨されています。UNDOアドバイザはOracle Enterprise Managerや、PL/SQLのDBMS_ADVISORパッケージから使用できます。詳細はマニュアル等をご確認いただければと思いますが、UNDOアドバイザの仕組みとしては、実際の想定されるワークロード(一連の業務処理)を一通り流して、その結果として推奨されるUNDO表領域のサイズを知るというものとなります。ですので、精度の高い見積もりにはなりますが、オブジェクトや想定されるデータ量でのレコード、実行されるSQLなど、一通りの準備が出来て初めて見積もることができます。しかし、データベースの設計・構築時にそれを行うことはできず、通常はある程度下流工程になってきた段階で可能になるため、これでは鶏と卵みたいな話になってしまいます。ですので、最終的にはUNDOアドバイザで見積もり値を確認するのは望ましいのですが、最初はある程度概算で算出する必要があります。

以下は私のかなり個人的な見解となりますが、まず、現行機能踏襲するようなリプレイス案件やポーティング案件であれば、とりあえず、以前のUNDO表領域サイズより気持ち大きめに取り、後にUNDOアドバイザで様子を見ていくということでもよいかもしれません。これはかなり楽なパターンです。もし、全くの新規開発ということであれば、PoCで精度よく検証でもできない限りは事前に見積もることはかなり困難で、類似案件でのサイズや自らの経験をもとに、いわゆるKKDでいったん出すしかないと考えています。

もし、どうしても真面目に出そうとするのであれば、

  • A. 一番実行が長そうなSELECT文の実行時間(秒)
  • B. 単位時間(秒)当たりに生成されるUNDOブロック数

を仮定して、作成するデータベースで採用するブロックサイズをCとしたときに、A×B×Cを仮の値として算出します。

Aの一番実行が長そうなSELECT文の実行時間の仮定については、簡単な例ですが1000万件のトランザクション表や1000件のマスタ表が複数あるとして、それらを結合するようなSELECT文の実行があると想定される場合、もし手元で確認できるOracle Database環境があれば、想定されるレコード長、レコード件数で単純なテーブルを作成してそのSELECTにどれくらい掛かるのか見当をつけておきます。

Bの単位時間(秒)当たりに生成されるUNDOブロック数の仮定についても、同様に、もし手元で確認できるOracle Database環境があれば、一番更新が多いと思われる一定期間内に想定される更新処理を行い、それによってどれだけのUNDOブロックが発生するかを計測し、そのブロック数を更新実行時間で割って単位時間(秒)当たりに生成されるUNDOブロック数を仮定します。なお、確認で使用するデータベースのブロックサイズは同じである必要があります。

生成されるUNDOブロック数も先ほどご紹介したV$UNDOSTATを用いて計測できます(UNDOBLKS列)。

-----①生成されたUNDOブロックを確認(a)-----
SQL>select
  to_char(BEGIN_TIME,'YYYYMMDD HH24:mi:ss'),
  to_char(END_TIME,'YYYYMMDD HH24:mi:ss'),
  UNDOBLKS
from V$UNDOSTAT where BEGIN_TIME >= SYSDATE-1/24 order by 1;

TO_CHAR(BEGIN_TIM TO_CHAR(END_TIME,   UNDOBLKS
----------------- ----------------- ----------
…
20180815 12:04:42 20180815 12:10:00          20

-----②上記確認後、別セッションにて、一定期間内に想定される更新処理を実施(b)----- 

-----③生成されたUNDOブロックの確認(c) -----
SQL> / 

TO_CHAR(BEGIN_TIM TO_CHAR(END_TIME,   UNDOBLKS
----------------- ----------------- ----------
…
20180815 12:04:42 20180815 12:14:42      86642
20180815 12:14:42 20180815 12:23:52     140459

まず、V$UNDOSTATの最新のレコードで更新処理実施前のUNDOブロック生成数を確認します。(a)
次に、別セッションにて一定期間内に想定される更新処理を実施します。(b)
最後に、今一度、V$UNDOSTATを確認します。(c)

この(c)において、最初に確認したレコードでのUNDOBLKSの増加分と、それ以降の最新のレコードまでのUNDOBLKSのすべてが、今回の想定される更新処理で生成されたUNDOブロック数になります。上記例でいいますと、(86642-20)+140459=227081ブロックとなります。
そして(b)の一定期間の想定が10分だとした場合、単位時間(秒)当たりに生成されるUNDOブロック数は227081/600≒379ブロックとなります。

そして、先ほどの式に戻り当てはめてみますと、

  • A. 一番実行が長そうなSELECT文の実行時間(秒)が例えば3600秒(60分)であったと想定し
  • C. 作成するデータベースで採用するブロックサイズが8Kである場合、
  • A×B×C = 3600 × 379 × 8K = 約10.4GB

となります。細かい話をしますと、UNDO管理する上でのメタデータなども内部的には生成されますので、余裕率込みでキリのよいサイズに切り上げたものを一旦のUNDO表領域サイズの候補とします。ここでは12GBあたりが候補でしょうか。昨今の大容量時代、この程度のサイズであればもう少し余裕をみて16GBぐらいでもよいかもしれません。

繰り返しとなりますが、本来は開発初期に事前に見積もることはかなり困難です。上記見積もりにおいては、そもそも更新処理と一番実行が長そうなSELECT文は同時に行われるのかとか、手元の環境で試すといってもマシンスペックが違うなど、突っ込みどころはいろいろあります。やはり最終的には、本番で想定される件数・処理で行われるテストにおいて見直していただければと思います。

(補足)更新処理におけるUNDO生成量は、件数はもちろんのことその処理内容によっても大きく変わってきます。特に覚えておきたいのはdelete文で表の全件を削除しようとすると、その表のレコードすべてのUNDOデータが生成されます。同じ全件削除でも、ロールバックできないtruncateであればそのようなことはありません。もちろん処理要件によっては、以降の処理次第でロールバックしなければいけなくなる場合もあるでしょうから、一概にdeleteでの全件削除が良くないとはいえませんが、件数の多いdeleteはUNDOデータを多く生成するということは覚えておきたい点になります。

おわりに

遅ればせながら、ここ最近機械学習やAIといった分野に非常に興味がわいてきて勉強を始めているところではありますが、その一方で、日々現場では、普遍的なOracle Databaseの知識、技術を問われることはまだまだ多いです。自分自身理解が怪しかったりすることもありますので、免許更新ではないですが、テーマが出るごとに腑に落としていきたいと思っており、またこのコラム内でも紹介できればと思っています。

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

番外編~私の散歩道~

私は若干引いたところから見る新宿が大好きで、よく散歩やジョギングのコースにしています。左側の写真は神田川にかかる大久保通りの橋から見た新宿で、夜景がかなり美麗です。右側の写真は渋谷区の北の外れにあるグラウンドから見た新宿で、今は無くなってしまいましたが実は私が卒業した小学校がそこにありました。当時の卒業文集にも屋上から見た新宿の絵を描いたのですが、そこにはまだ都庁はありませんでした。(あ、年齢がばれてしまう)
アニメ映画監督の新海誠さんの映像にも若干引いたところからの新宿がよく出てきますが、映像美も相まってどこか切なくなります。新宿を眺める絶景ポイントはまだまだあると思いますので、これからも探してみたいなと思っています。