皆さんこんにちは、今年も梅雨の季節を迎えましたね。じめじめして嫌な気候ですが負けずに頑張りましょう。
最近、インメモリ・データベースやカラム型データベースなどが話題になっていますが、OracleデータベースもOracle Database 12c(Oracle12c)からOracle Database In-Memory(DBIM)を提供していますので、今回はそのDBIMについて説明しようと思います。後半に、Oracle12cで拡張されたデータベース・バッファ・キャッシュ(DBバッファ・キャッシュ)の機能についても説明していますので、参考にしてください。
1. Oracle Database In-Memoryとは
DWHなどの大量データを高速に処理するにはパラレル実行が欠かせませんが、パラレル度が多いとI/O性能のボトルネックになりやすいという問題があります。そこで、最近の搭載メモリが多いサーバーを有効利用するために、メモリにデータを載せてI/Oを発生させないインメモリ・パラレル実行(In-Memory PX)がOracle Database 11gR2から提供されましたが、これまでの行型フォーマットでは処理性能に限界がありました。そこで次に、分析処理などではアクセスする列が少ないことから、圧縮効率やアクセス効率の良い列型フォーマットをメモリ上で使用することにしました。それがOracle12cから提供されたDBIMです。ただし、このような列型フォーマットにも以下のような問題点はあるので、今回はDBIMではどのように解決しているかなどを説明しようと思います。
- 圧縮効率が良いといってもメモリ容量には限界があるので、すべてのシステムで使用できる訳ではない
- 列型フォーマットは、多くの列や少ない行数のアクセスは効率が良くない
- 格納する容量を増やすために圧縮するが圧縮解除にCPUのオーバーヘッドが発生する
- インメモリといっても大量の行数のフィルター操作ではCPUネックになりやすい
最初の二つについてはデュアル・フォーマット、残りの二つについてはインメモリ圧縮とインメモリ・スキャンによって効果的に処理できるようになっています。それでは、それぞれについて説明していきます。
(1)デュアル・フォーマット
まずは、デュアル・フォーマットについて説明します。
データベースの格納方法には、行型フォーマットと列型フォーマットがありますが、それぞれにメリットやデメリットがあります。ただし、どちらかを選択して使用する必要があるので、すべて速くできない場合もありました。そのため、DBIMでは、メモリ上に列型フォーマット(IM列ストア)と行型フォーマット(DBバッファ・キャッシュ)を保持して、どちらにもアクセスできるデュアル・フォーマットを採用しています(どちらをアクセスするかは、オプティマイザが自動的に判断します)。例えば、以下のように、定型処理は行型フォーマットで索引アクセス、非定型処理は列型フォーマットで全表スキャンするということもできます。

このとき、以下のようなメモリ・サイズの問題により、すべてで使用できないのではという疑問を持つかと思います。
- 二つの形式のデータをメモリ上に格納するため、2倍のメモリが必要になってしまう
- 列型フォーマットはメモリ上だけに持つので、データ・サイズによっては使用できない
これについては、表またはパーティションなどの単位でIM列ストアの使用を指定できるので、すべてのデータを二つの形式で持つ必要はなく、柔軟に使用できるようになっています。また、列型フォーマットは圧縮効果が大きいことや、DBバッファ・キャッシュにはすべてのデータを格納する必要はないなどで、少ないメモリでも効果的に格納できるようにもなっています(以下のように、表、パーティション、列の単位で、IM列ストアの使用と未使用を指定することができます)。
ALTER TABLE <表名> [MODIFY PARTITION <パーティション名>] INMEMORY [<インメモリ圧縮>] [(<列名>[,…])] –- IMの使用 ALTER TABLE <表名> [MODIFY PARTITION <パーティション名>] NO INMEMORY [(<列名>[,…])] -- IMの未使用
このとき、二つの形式でパーティション表を作成すると、二つの形式にアクセスするSQLでは効果的にアクセスできないように思いますが、これも第34回で説明した表拡張(Table Expansion)が使用できるので問題ありません。例えば、以下のように二つのパーティション(パーティション1が列型で、パーティション2が行型)にアクセスするSQLを実行すると、表拡張によりUNION ALLを使用した二つの問合せに変換します(TABLE ACCESS INMEMORY FULLがインメモリ・スキャンになります)。
—————————————————————–<省略>—————–
| Id | Operation | Name | | Pstart| Pstop |
—————————————————————–<省略>—————–
| 0 | SELECT STATEMENT | | | | |
| 1 | VIEW | VW_TE_2 | | | |
| 2 | UNION-ALL | | | | |
| 3 | PARTITION RANGE SINGLE | | | 2 | 2 |
| 4 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| TAB1 | | 2 | 2 |
|* 5 | INDEX RANGE SCAN | TAB1_IX1 | | 2 | 2 |
| 6 | PARTITION RANGE SINGLE | | | 1 | 1 |
|* 7 | TABLE ACCESS INMEMORY FULL | TAB1 | | 1 | 1 |
(2)インメモリ圧縮
次に、インメモリ圧縮について説明します。
圧縮は、領域を削減するための機能で、問合せ時に圧縮解除のオーバーヘッドが発生して、性能ダウンするように思われていますが、IM列ストアでは圧縮解除せずに、スキャンとフィルター操作を行うことができます(これをディクショナリ圧縮と呼びます)。そのため、圧縮された少ないデータで実行することができるので、問合せパフォーマンスも向上します。ただし、IM列ストアに格納する処理(ポピュレート処理)では、オーバーヘッドが発生するので注意してください。インメモリ圧縮には、以下の6つのレベルがあり、用途に合わせて使用できるようになっています。ただし、FOR CAPACITYでは、ディクショナリ圧縮は使用されません。
- NO MEMCOMPRESS(データを圧縮しない)
- MEMCOMPRESS FOR DML(DMLのパフォーマンスを最適化する圧縮)
- MEMCOMPRESS FOR QUERY LOW(問合せパフォーマンスを最適化する圧縮で、これがデフォルトです)
- MEMCOMPRESS FOR QUERY HIGH(問合せのパフォーマンスと領域削減を最適化する圧縮)
- MEMCOMPRESS FOR CAPACITY LOW(領域削減に比重をおいた圧縮)
- MEMCOMPRESS FOR CAPACITY HIGH(最も領域削減に比重をおいた圧縮)
Oracle Compression Advisorは、インメモリ圧縮をサポートするようにOracle12c(12.1.0.2)で拡張されているので、事前に圧縮率を見積もることができるこれを参考にするのが良いでしょう。
(3)インメモリ・スキャン
次に、パフォーマンス向上に重要なインメモリ・スキャンについて説明します。
IM列ストアでは、問合せに必要となる列にだけアクセスし、この列を圧縮解除せずにWHERE句のフィルター条件を直接適用することができます。そして、フィルター条件に対して、以下のインメモリ・ストレージ索引とSIMDベクトル処理を使用することで、さらに効果的に処理できるようになっています。
- インメモリ・ストレージ索引
各列は、複数のインメモリ圧縮ユニット(IMCU)で構成され、各IMCU(1Mバイトの倍数単位)に最小値/最大値を自動的に記録します。これにより、検索条件に合致するIMCU領域だけを読込むことで、効果的にアクセスすることができます(パーティション・プルーニングと同じような動作を提供します)。以下の例は、検索条件が’storeid > 8’になっているので、その値が存在しない最初の二つのIMCUはアクセスが回避されています。ただし、データの格納順によって効果が異なるので注意が必要です(先頭からカーディナリティの低い順番に列をソートして格納することで効果を大きくできます)。そのため、パーティション表と併用するのがより効果的だといえます。 - SIMDベクトル処理(SIMDベクター処理)
IM列ストア内のスキャン対象のデータに対して、DBIMでは最新のプロセッサで搭載されているSIMD(Single Instruction Multiple Data)命令セットを使用して、列内の各エントリを一つずつ評価する代わりに、列値のセットを単一のCPU命令でまとめて評価できます。例えば、以下のような4回の条件比較の場合、SIMDでは1命令で行うことができるので、高速にフィルター条件のチェックなどを行うことができます。このとき、CPU使用率も削減されます。

(4)その他の機能
最後に、その他の機能としてベクター結合とベクターGroup Byについて簡単に説明します。
これは、IM列ストア固有の機能ではありませんが、IM列ストアと一緒に使用することで、SIMDベクター処理によってさらに効果的に処理できるようになっています。
- ベクター結合
第22回で説明したブルーム・フィルターを使用して実行しますが、SIMDベクター処理も行われるので、さらに効果的になっています。Oracle12c(12.1.0.2)からIM列ストアに対してシリアル処理でも動作するようになっています。 - ベクターGroup By
スター・スキーマのような大きい表と複数の小さい表に対する結合は、スター型変換(Star Transformation)やブルーム・フィルターによって効果的に行いますが、複雑な集計(Group By)も存在するときに、Group Byも含めてさらに効果的に行うようにした機能です(この機能も行型フォーマットで使用できますが、DBIMオプションは有効にする必要があります)。これについては、スター型変換なども含めて次回に説明しようと思います。
このように、DWHや分析処理には非常に使いやすい機能になっていますが、すべての処理で効果が大きい訳ではないので、間違わないようにしてください。I/Oやフィルター操作が多い処理では効果が大きいので、機会があれば使用を検討してみてください。
2. Oracle Database 12cのDBバッファ・キャッシュ
搭載メモリが多いサーバーを効果的に使用するように、Oracle12c(12.1.0.2)からDBバッファ・キャッシュに対する以下の機能が提供されているので、メモリ関連として少し紹介しておきます。
- Automatic Big Table Caching(自動ビック表キャッシュ機能)
- Force Full Database Caching(全データベース・キャッシュ強制機能)
(1)Automatic Big Table Caching(自動ビッグ表キャッシュ機能)
DBバッファ・キャッシュは、第13回で説明したようにDBブロックをLRUアルゴリズムで管理されているため、第20回で説明したIn-Memory PXの全表スキャンとそれ以外のアクセス(索引スキャンなど)が混在するような環境ではDBバッファ・キャッシュの競合が発生して、効果的に動作しない場合があります。
そのため、Oracle12cからIn-Memory PXが動作する大規模表(第13回で説明したLong tables)に対して、専用のキャッシュ領域を指定できるようになりました(デフォルトはゼロで無効です)。このビッグ表キャッシュ領域を指定することで、索引スキャンなどと競合しない以外に、以下の動作によりさらに使いやすくなっています。
- すべての大規模表がキャッシュに収まらない場合
アクセス・パターンに基づいて、ブロック・レベルではなくオブジェクト・レベルで、どの表をキャッシュするかを判断します(フル・スキャンする表は、ブロック・レベルでキャッシュに載せてもあまり意味がないからです)
- 一つの大規模表のデータすべてがキャッシュに収まらない場合
無駄なデータをDBバッファ・キャッシュに読込まないように、入り切らない一部のデータをディスクに残してダイレクト・パス・リードを行います(このようなデータをすべてDBバッファ・キャッシュ経由でアクセスすると、キャッシュされていないデータにアクセスするときに、別のデータをディスクに書込むスラッシングと呼ばれる効率の悪い現象が発生するからです)
このビック表キャッシュ領域は、シリアル実行で有効化するには、初期化パラメータDB_BIG_TABLE_CACHE_PERCENT_TARGET(DBバッファ・キャッシュに対する割合)を設定する必要があります。さらに、パラレル実行で有効化するには、In-Memory PX(初期化パラメータPARALLEL_DEGREE_POLICYを’AUTO’または’ADAPTIVE’に設定)を使用する必要があります。Oracle RAC環境では、パラレル実行だけサポートされるため、両方の設定が必要です。
(2)Force Full Database Caching(全データベース・キャッシュ強制機能)
第13回で説明したように、大きな表を全表スキャンしたときに、有効なデータがDBバッファ・キャッシュから削除されないようにキャッシュされません。ただし、全データベースをキャッシュするのに十分な領域がDBバッファ・キャッシュにあると、キャッシュした方が効果的な場合があります。そのため、Oracle12cから明示的に全データベースをキャッシュする強制モード機能が提供されました(これまでは、Oracleデータベースがキャッシュした方が効果的と判断したときだけ自動的にキャッシュしていました)。この強制モードのときには、NOCACHEモードのLOBやSecureFilesのLOBもキャッシュされるので、必要な場合には使用を検討してください。
全データベース・キャッシュを強制モードにするには、マウント状態のときに以下のように行います。
SQL> ALTER DATABASE FORCE FULL DATABASE CACHING; SQL> ALTER DATABASE OPEN;
強制モードかは、以下のようにビュー’V$DATABASE’の列’FORCE_FULL_DB_CACHING’で確認できます(’YES’が強制モード、’NO’がこれまでと同様のデフォルト・モードです)。
SQL> SELECT force_full_db_caching FROM v$database; FORCE_FULL_DB_CACHING --------------------- NO
ただし、この強制モードを有効にしても、データベースをDBバッファ・キャッシュに強制的に入れられる訳ではなく、データベース全体がDBバッファ・キャッシュに完全にキャッシュされる対象となるだけです。そのため、Oracleデータベースは、表がアクセスされたときにキャッシュします。
3. おわりに
今回はOracle Database In-MemoryとOracle12cのDBバッファ・キャッシュについて説明しましたが、少しは参考になりましたでしょうか。これからも頑張りますのでよろしくお願いします。
それでは、次回まで、ごきげんよう。
