exadataと遅延ブロッククリーンアウトとシングルブロックリード [アーキテクチャ]

exadataにおいて、フルスキャンで読み取り一貫性を発生させない(つまりUNDOへの読み込みを発生させない)ことが遅延対策のポイントになるのは言うまでも無いが、それ以外にも遅延ブロッククリーンアウトが遅延の原因となるケースに遭遇したのでメモしておく。

事象としては、1GB程度のテーブルをcsvに出力する処理(夜間バッチ)が50分(SQLレポートのelapsed time)かかったのである。クエリは単純な1テーブルへアクセスするもので母体の件数は600万件、WHERE句でフラグでフィルタしているので、出力は500万件程度。SQLトレースを取得すると、大量のシングルブロックリード(20GB以上)が発生しており、ASHからUNDO表領域へのアクセスであることが判明した。日中帯に同じSQLを実行したら16秒程度(sqlplusからautotrace traceonlyで測定)で完了する。このときスマートスキャンが発生していた(SQLトレースから確認)していたため、通常であればそもそもシングルブロックリードが発生するはずがない。このテーブルは事前にトランケート・インサートされた後にこのクエリが走るだけで、他のトランザクションはないため、読み取り一貫性によるUNDOへのアクセスが発生していた可能性はない。しかも母体のテーブルサイズをはるかに上回るUNDO量である。これは一体どういうことか。。。

一般的なOracle(非Exadata)ではFTSはマルチブロックで読み込んだブロックに対して読み取り一貫性のため必要なUNDOを読み込みCRブロックを生成する。スマートスキャンの恩恵は受けられないが、IOはマルチブロックリードにより効率的に行われる。一方、exadataではFTSはスマートスキャンとなることがあり、その場合は各ストレージサーバへFTSの処理がオフロードされる。このとき、各ストレージサーバはCRブロックを返却する必要があるが、各ストレージサーバは各ブロックが「CRブロックである」ことを認識し、ストレージサーバ毎に独立してプレディケートフィルタやカラムプロジェクションを行うのである。しかし、「CRブロックでない」、つまり読み取り一貫性の為にUNDOを読み込む必要があると、ストレージサーバはスマートスキャンを諦めてシングルブロックを返却する。そして、DBサーバ上のバッファキャッシュ上で必要なUNDOブロックを読み込み(シングルブロックリード)CRブロックを生成することを繰り返す。スマートスキャンの恩恵を受けるため、他トランザクションからの更新を避けるのはこのためである。

実はこの挙動は遅延ブロッククリーンアウトでも発生する。遅延ブロッククリーンアウトは、簡単に言うと最新化されていない未コミット状態のブロックをコミット時ではなく、後でコミット済みに変更するメカニズムである。これが発生すると、ブロック読み込み時に、そのブロックがコミット済みかどうかを確認するために、ITLに記録されるトランザクションID(xid)から、UNDOセグメントヘッダのトランザクション表へアクセスする。コミット済みであることが確認できれば、そのブロックのITLをコミットSCNとあわせてコミット済みに変更する。ブロックへの変更が発生するため、(たとえSELECTであっても)REDOが発生する。

また、UNDOセグメント毎のトランザクション表のスロット数はOracleのバージョンやオプションにより決まる(34,48,96個等)。また、1つのUNDO表領域上に作成できるUNDOセグメント数にも上限があることから、必然的にトランザクション表のスロットは上書きされてしまう。このため、コミット時のSCNを確認するため、トランザクション表への読み取り一貫性、つまりUNDOをたどって過去のSCNを確認するという挙動が発生することもある。これらUNDOへのアクセスは、すべてDBサーバからシングルブロックリードが必要である。

ストレージサーバはCRブロックであることをどのように判断するのだろうか。各ブロックのITLにはそのブロックにアクセスするトランザクション(xid)とその状態(コミット済みがどうか等)、コミット済みSCN、関連するUNDOブロックアドレスが記録されている。したがって、オフロードされたクエリのSCNがコミットSCNより新しければ、そのブロックはCRブロックと判別できるはずである。未コミットのトランザクションがある場合、またはコミット済みでもクエリのSCNがコミットSCNより古い場合はUNDOブロックを読む必要があるので、シングルブロックを返却する流れとなる。実際はもっと複雑な流れだとは思うが、おおまかにはこんなところだろう。

では、遅延ブロッククリーンアウトの状況ではどうだろうか。遅延ブロッククリーンアウトは、コミット済みであるにもかかわらず、ブロックは未コミット状態となっている状況である。上記ロジックが正しいとすれば、この状況は必ずUNDOの読み込みが必要となるため、スマートスキャンはできなく、シングルブロックを返却する流れとなることは容易に想像がつく。

遅延ブロッククリーンアウトが発生する要因の一つは、バッファキャッシュの枯渇である。未コミットのダーティブロックが、トランザクション完了(コミット)前にDBWRによりパージされてしまうと、トランザクション完了してもディスク上には未コミット状態のままとなる。このため、更新時のバッファキャッシュを増加させれば発生頻度を減らすことが可能である。もちろん、コミット間隔を小さくすることも対策として有効だろう。また、UNDOへのアクセスを減らすという意味では、トランザクション表(のスロット)が上書きされなければ、トランザクション表に対するUNDO読み込みは不要となる可能性があるため、更新直後に読み込む、という対処も有効なはずである。

あくまで仮設であるが、今回の遅延事象は、INSERT時(2000件単位でコンベンショナルINSERT・コミット)のバッファキャッシュ上のブロックが、他処理との並走によりバッファキャッシュが不足し、未コミット状態でディスクに反映されたものが増えた。また、コミット後からクエリまで2時間程度経過していたことから、トランザクション表が上書きされ、コミットSCNを確認するためにトランザクション表のUNDOを読み込む必要がある状態になっていた。このため、後続のフルスキャンで遅延ブロッククリーンアウトが発生し、UNDOへの読み込みが必要なことからスマートスキャンは効かず、UNDOへのシングルブロックリードを発生させたのではないか、と考えた。実際、バッファキャッシュを2倍程度に変更することで、50分かかっていたクエリが10秒以下にすることができた。簡単に2つの処理の違いを比べると、如何にExadataにおいて遅延ブロッククリーンアウトの際のオーバーヘッドが大きいかわかる。

◆通常のスマートスキャンの挙動
フルスキャン→ダイレクトパスリード→スマートスキャン(マルチブロックリード)→ストレージオフロードによりフィルタ済みブロックのみ返却→結果セット

◆遅延ブロッククリーンアウトの際の挙動
フルスキャン→ダイレクトパスリード→スマートスキャン→ITLが未コミットなのでシングルブロックで返却→UNDOセグメントヘッダ読み込み→コミットSCN確認のためトランザクション表のUNDO読み込み→コミットクリーンアウト→UNDO変更のREDOチェンジベクター生成→バッファキャッシュ上でフィルタ処理→結果セット

遅延ブロッククリーンアウトが発生したことを示すことは一般的に難しいとされる。今回はAWRやASHから得られる情報から消去法的に仮設を作り上げた。ただ、それでもなお、今回なぜこれだけの量のUNDOを読む必要があるのかまでは追いきれなかった。この事象を通して遅延ブロッククリーンアウトについて改めて理解を深めることができたのは、やはりJonathan Lewis氏のOracle Coreによることが大きい。Exadataや最新のOracleバージョンには記載はないが、根本のアーキテクチャは変わらないものである。

わかればわかるほど、わからないことを知る、ということを感じる今日この頃である。

以上

◆参考

Oracle Core: Essential Internals for DBAs and Developers (Expert's Voice in Databases)

Oracle Core: Essential Internals for DBAs and Developers (Expert's Voice in Databases)

  • 作者: Jonathan Lewis
  • 出版社/メーカー: Apress
  • 発売日: 2011/12/06
  • メディア: ペーパーバック



nice!(0)  コメント(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

※ブログオーナーが承認したコメントのみ表示されます。