津島博士のパフォーマンス講座 
第54回 Oracle Database In-Memoryについて(2)

■津島博士による解説が動画でも! セミナー動画公開中です。
津島博士のパフォーマンス講座「パフォーマンス問題はなぜ起きるのか」  【WMV】 【MP4】 【PDF

皆さんこんにちは、梅雨のジメジメがまだ続くと思っていたら、もう真夏のような感じですね。この原稿が公開される頃には関東でも梅雨明けですね。 今回は、Oracle Database In-Memory(DBIM)の続きとして、DWHなどでよく使用するスター・スキーマとそれに対する問合せ処理について説明しようと思います。スター・スキーマは、高速に処理しようとすると難しいところがありますので、DBIMでどのように改善しているかも含めて説明します。後半に、DBIMのポピュレートと再ポピュレートについても説明していますので、参考にしてください。

1. スター・スキーマの処理

DWHや分析処理でよく使用されるのが、以下のような大きな表(ファクト表)と複数の小さな表(ディメンジョン表)で構成されているスター・スキーマです。これは分析するデータ(売上金額など)をファクト表に時系列で格納して、効果的に検索することを目的にしています。また、分析の条件をディメンジョン表に持って使いやすいので、BIツールなどで幅広くサポートされています。

pic 1

ただし、以下のような特徴などにより、効果的に結合処理を行うのが簡単ではありません。

  • ディメンジョン表同士は結合されていないので、ファクト表を2番目に結合(またはディメンジョン表を直積してからファクト表と結合)する必要がある。
  • ファクト表を直接フィルター条件で絞り込めないので、結合しないとファクト表の行数を削減できない。

Oracleデータベースでは、第46回で説明したRight-deep Joinでハッシュ結合することで、以下のようなSQL(店舗地域と商品ブランド別の合計売上金額を求める)でも多少は効果的に処理できますが、ファクト表の行数が多いとどうしても結合処理の負荷が大きくなってしまいます。

pic 2

やはり、ファクト表の行数が多いときには、結合する前に行数を削減しないと高速に処理することができません(このようなことからDWH専用のデータベースなどが存在するのでしょう)。そのため、Oracleデータベースでは、このようなスター・スキーマを効果的に処理するように、以下の機能が提供されたということです。この違いを知っていると、スター・クエリーを効果的に実行できるようになると思います。

  • スター型変換(Star Transformation)
  • 複数のブルーム・フィルター(Multiple Bloom Filter)
  • ベクターGroup By(Vector Group By)

それでは、先程のSQLを使用して、それぞれについて説明していきます。

(1)スター型変換(Star Transformation)
まずは、Oracle8から提供されたスター型変換から説明します。
これは、ビットマップ・スター・ジョイン(ビットマップ索引を使用したスター型結合)に問合せ変換する機能になります。STAR_TRANSFORMATION_ENABLED初期化パラメータを'TRUE'か'TEMP_DISABLE'に設定することで、以下のようにSQLの変換を行います(デフォルトは'FALSE'です)。このときビットマップ・セミ結合述語と呼ばれる副問合せ述語が追加されます(赤字の箇所です)。これが動作するには、ディメンジョン表とファクト表に主キーと外部キーで参照整合性制約が必要です。また、ファクト表の外部キー列に、それぞれビットマップ索引を作成する必要もあります。

pic 3

このスター型変換は、以下のような手順で実行されます。

  • ① ディメンジョン表に対して、フィルター条件でスキャンして一時表変換を行います(このとき、STAR_TRANSFORMATION_ENABLED初期化パラメータを'TEMP_DISABLE'にすることで、一時表変換は行いません)。ディメンジョン表のフィルターが多いときは、一時表に変換した方が効果的です。
  • ② 変換された一時表(またはフィルター条件でスキャンしたディメンジョン表)からファクト表の結合列のビットマップ索引を絞り込んで、そのビットマップのすべてをAND条件でマージします。
  • ③ そのビットマップをROWIDに変換して、ネステッド・ループ結合でファクト表をランダム・アクセスします。
  • ④ 最後に、必要なディメンジョン表のデータを求めるために結合します(①で一時表変換を行っていると、ディメンジョン表の後戻り結合が回避されます)。

pic 4

この処理の特徴は、ファクト表にランダムI/Oをすることで、結合する行数を削減するということです。ランダムI/Oは、アクセスするデータが少ないときは効果的ですが、そうでないとアクセス効率が悪くなってしまうので、すべてで最適という訳ではありません。また、ビットマップ索引が必要になるので、データをロードするときなどのメンテナンスも必要です。

(2)複数のブルーム・フィルター(Multiple Bloom Filter)
次に、Oracle Database 11gからのブルーム・フィルターを使用した処理について説明します。
Oracle Database 10g以前は、スター型変換を使用する必要がありましたが、ビットマップ索引の作成とランダムI/Oになるなどの問題がありました。そこで、ブルーム・フィルターを使用して、結合前に行数を削減するようにしたのがこれです。
スター・スキーマのときのブルーム・フィルターは、Right-deep Joinと同時に行うことで、より効果的に処理するようになります(Right-deep Joinでファクト表を最後にハッシュ結合するようにして、ブルーム・フィルターで結合前に行数を削減します)。このような複数の表を結合する場合には、Multiple Bloom Filterが動作するようになっています。複数のフィルターを同時に使用することができるので、ファクト表の行数を結合前に大幅に削減することができます。
この複数のブルーム・フィルターは、以下のような手順で実行されます。

  • ① ディメンジョン(商品)表をフィルター条件でスキャンして、ハッシュ・テーブルとブルーム・フィルター(BF0000)を作成します。
  • ② ディメンジョン(店舗)表をフィルター条件でスキャンして、ハッシュ・テーブルとブルーム・フィルター(BF0001)を作成します(このように必要なディメンジョン表のブルーム・フィルターを作成していきます)。
  • ③ ファクト表を全表スキャンするときに、作成したそれぞれのブルーム・フィルターを使用して行数を削減します。
  • ④ 最後に、それぞれのディメンジョン表のハッシュ・テーブルを使用してハッシュ結合を行います。このハッシュ結合をすることで、ブルーム・フィルター時に誤検出された行は除外されます。

pic 5

この実行計画は、DBIMのベクター結合ですが、DBIMを使用しないときでもパラレル実行を行うと動作します。

(3)ベクターGroup By(Vector Group By)
最後に、DBIMで追加されたベクターGroup Byについて説明します。
結合では行数がそんなに削減されない場合もあるので、分析処理で多く使用されるGroup Byも同時に行うようにしたのがこの機能です。スター・スキーマに対して、Group Byで大幅に行数が削減されるときに、DBIMを使用することでさらに効果的に処理することができます。
このベクターGroup Byは、以下のような手順で実行されます。

  • ① ディメンジョン表をフィルター条件でスキャンして、KEY VECTOR(商品:KV0000、店舗:KV0001)の作成と一時表変換を行います(スター型変換と同じように、ディメンジョン表の後戻り結合を回避するために行います)。KEY VECTORには、結合列のフィルター以外にGroup Byも行うため、グルーピング列値も格納されます。
  • ② ファクト表を全表スキャンするときに、作成したそれぞれのKEY VECTORを使用して、行の絞り込みとGroup Byを行います(結合する前にGroup Byを行うので、ブルーム・フィルターとは異なり、フィルター時に誤検出されないようになっています)。
  • ③ 最後に、必要なディメンジョン表のデータを求めるために結合します。

pic 6

ベクターGroup Byは、オプティマイザが最適と判断されたときに動作しますが、VECTOR_TRANSFORMヒントで強制的に実行させることもできます(IM列ストアに存在しない表では選択されにくくなっています)。

このように、様々な結合方法がありますが、DBIMを使用しているときにはベクターGroup Byが最も効果的に動作することが分かると思います。

2. ポピュレートと再ポピュレート

DBIMは、DWHなどの問合せには非常に有効な機能ですが、難しくしているのがこのポピュレート(移入)と再ポピュレート(再移入)だと思います。
ポピュレートは、CPUに負荷のかかる処理になるので、どのように動作させるかは注意が必要です。また、リアルタイムで更新を行う場合には、どのようにIM列ストアを更新するかによって、大きく性能に影響してきます。そのため、ここからは表またはパーティション単位にIM列ストアに格納するポピュレートと、更新したときに非同期でIMCU(インメモリ圧縮ユニット)単位に動作する再ポピュレートについて少し説明します。

(1)ポピュレート
まずは、ポピュレートから説明します。
DBIMを使用するには、IM列ストアにデータを格納する必要がありますが、すべてのデータをIM列ストアに格納しない場合、どのオブジェクトを格納するかを設定する必要があります。そのため、DBIMでは、表などにINMEMORY属性が追加され、これを指定しているオブジェクトだけが、IM列ストアにポピュレートされます(INMEMORY属性は、表領域、表、パーティションなどで指定できます)。
指定されたオブジェクトは、INMEMORY属性の優先順位レベルによって、データベースのオープン後(またはCREATE TABLE/ALTER TABLE時)や、初回スキャン時にポピュレートされます。優先順位レベルには、以下の5つのレベルがあるので、アクセス頻度や性能重視のオブジェクト順位などで設定できるようになっています。

pic 7

ダイレクト・パス・ロードの場合でも、優先順位レベルによってポピュレートされます(優先順位レベルが、NONE以外のときには自動的にポピュレートされますが、NONEでは上記と同じように次回問合せ時に行われます)。また、パーティション表に対して、交換する表をINMEMORY属性でポピュレートしてからEXCHANGE PARTITIONを行うことで、最小限の影響でパーティションにデータをポピュレートできます。他のシステムからデータをロードするようなDWHでは、このように行うのが最適だと思います。
ポピュレートは、INMEMORY_MAX_POPULATE_SERVERS初期化パラメータに指定した数で、バックグランド・ワーカー・プロセスが動作します(デフォルトは"CPU_COUNT×0.5"または"PGA_AGGREGATE_TARGET÷512M"のいずれか少ない方です)。
Oracle RAC(Real Application Clusters)環境では、これ以外にも指定がありますが、それについてはまたの機会に説明しようと思います。

(2)再ポピュレート
次に、更新したときに動作する再ポピュレートを説明します。
一般的なDMLでは、コミットされてもIM列ストアに直ぐに反映されるのではなく、非同期で再ポピュレートを行います。再ポピュレートは、ポピュレートのように表単位などではなく、無効化されたデータを含むIMCUだけを再作成します。
IM列ストアにDMLで更新が行われると、更新内容をIMCUごとのトランザクション・ジャーナルに記録し、コミット時にIMCU内のデータを古いエントリ(無効)とします。これに対して問合せを行うと、最新データを返すことはできるので問題ありません。ただし、IMCU内の古いエントリは、トランザクション・ジャーナルまたは行型フォーマット(バッファ・キャッシュ)から取得されるので、古いエントリが多いほどIMスキャン性能は低下します。そのため、以下のタイミングでIMCUへ再ポピュレートが実行されます。

  • IMCU内の古いエントリがしきい値に達した時(再ポピュレート)
    しきい値(IMCUのアクセス頻度や古い行の数など)に達したときに、バックグラウンド・ワーカー・プロセスによって、自動的に実行されるオンライン操作です。
  • 2分間隔(トリクル再ポピュレート)
    2分ごとにIMCO(In-Memoryコーディネータ・バックグランド・プロセス)によって、自動的にポピュレート・タスクを実行するか(しきい値に達していない無効な行を含むIMCUが存在するか)チェックを行います。このときINMEMORY_TRICKLE_REPOPULATE_SERVERS_PERCENT初期化パラメータ(INMEMORY_MAX_POPULATE_SERVERS初期化パラメータに対する割合を指定)によって、再ポピュレートするIMCUの数を制限できます。最大値として50まで指定できますが、デフォルトはあまり負荷を掛けないように1になっています。実行させないときは0にします。
  • DBMS_INMEMORY.REPOPULATEプロシージャの使用
    表またはパーティション単位に、手動で再ポピュレートすることができます。

この再ポピュレートは、基本はデフォルトで問題ありませんが、更新が多いと問合せ処理が遅くなる場合もあります。また、ポピュレートは、圧縮効率が高いほど(CAPACITY HIGHが最も)負荷が大きくなるので、これも含めて調整するのが難しくなります。このチューニングについては、何度かTrial and Errorで行うことになりますが、これから少しずつまとめていきたいと思います。
DBIMは、いろいろな使い方ができるようになっていますが、まだまだチューニングが難しいところがあるので、まずは再ポピュレートのないような(単純なロードを行うような)、効果が大きいDWHなどのシステムから使用してみてください。

3. おわりに

今回はスター・スキーマの処理とOracle Database In-Memoryのポプュレートと再ポピュレートについて説明しましたが、少しは参考になりましたでしょうか。これからも頑張りますのでよろしくお願いします。
それでは、次回まで、ごきげんよう。