メモリー内キャッシュのサイズを正しく設定することは、ストアのパフォーマンス要件を満たすために重要な部分です。パフォーマンスの点から言うと、ディスクI/Oはコストの高い操作です。キャッシュからサービスできる操作が増えるほど、ストアのパフォーマンスはよくなります。
使用できるディスク・キャッシュ・ストラテジはいくつかあり、各々異なる作業負荷に適しています。ただし、Oracle NoSQL Databaseは、全データをメモリーに配置できないアプリケーション用に設計されたため、製品のこのリリースでは、その種の作業負荷に適したキャッシュ・ストラテジを示します。
続ける前に、重要な2つのキャッシュについて説明します。
JEキャッシュ・サイズ。Oracle NoSQL Databaseで使用されるベースとなるストレージ・エンジンはBerkeley DB Java Edition (JE)です。JEには、メモリー内キャッシュが備わっています。様々な制御が可能なため、ほとんどの場合、検討する必要のあるキャッシュ・サイズはこちらです。
ファイルシステム(FS)キャッシュ。最新のオペレーティング・システムでは、ディスクI/O専用のキャッシュやバッファを用意してI/Oサブシステムのパフォーマンスが向上するようになっています。FSキャッシュを使用すると、キャッシュに格納されているデータによって読取りが完結する場合、読取り操作を非常に高速に行うことができます。
JEでは、格納するデータの編成にBツリーが使用されます。Bツリーでは、木のようなデータ編成構造で高速な情報検索を実現します。木構造は、内部ノード(IN)とリーフ・ノード(LN)で構成されます。INは、データのナビゲーションに使用されます。LNは、Bツリーでデータが実際に格納される場所です。
Oracle NoSQL Databaseアプリケーションで使用される予定のデータ・セットが非常に大きいため、データの一部でさえJEのメモリー内キャッシュに格納することはあまりありません。したがって、最もよい方法は、データベースのINの大半(すべてではなくても)を保持するのに十分な大きさにキャッシュのサイズを設定し、ノードの残りのメモリーがシステム・オーバーヘッド(ごくわずか)とFSキャッシュに使用されるようにします。
INまたはLNがFSキャッシュからサービスされるかどうかは制御できないため、INの格納に十分な大きさにJEキャッシュのサイズを設定することのみがサイズ設定に関する注意事項です。INとLNの両方ともFSキャッシュを使用できます。FSキャッシュにある場合、INとLNにはJavaオブジェクト・オーバーヘッドがない(JEキャッシュの場合はある)ため、JEキャッシュ・メモリーよりもFSキャッシュ・メモリーの方が効率的に使用できます。
当然、この方法が十分に機能するには、データ・アクセス・パターンが完全にランダムではないようにします。有用なキャッシュ・ヒット率を達成するには、一部のキーと値のペアが他のペアよりも優先される必要があります。アクセス・パターンがランダムでなないアプリケーションの場合、LNとINのファイルシステム・キャッシュ・ヒット率が高いと、スループットが向上し、平均読取りレイテンシが低下します。また、適切にチューニングされている場合、ファイルシステム・キャッシュが大きいほど、ログ・ファイルへの順次書込みの際のストールの数が減り、書込みレイテンシが低下します。キャッシュが大きい場合、ほとんどの書込みを非同期に行うこともでき、スループットが向上します。
適度に集中したデータ・アクセス・パターンだと仮定すると、ここで説明したとおりにキャッシュのサイズを設定する場合、ディスク・サブシステムで次のようなスループットが実現される必要があります。
((readOps/Sec + createOps/Sec + updateOps/Sec + deleteOps/Sec) * (1-cache hit fraction))/nReplicationNodes => throughput in IOPs/sec
前述の概算では、作成、更新、削除の各操作がランダムI/O操作になると想定されています。ベースとなっているストレージ・システムのログ構造化特性から、これはあまりないことで、アプリケーション・レベルの書込み操作は、バッチ化された順次同期書込み操作になります。そのため、IOPs要件が誇張されることはありますが、前述の概算によって見積り用の控えめで適切な数値が得られます。
たとえば、シャードが2つ、レプリケーション係数が3(合計6個のレプリケーション・ノード)のKVStoreで合計2000操作/秒(すべての読取り、作成、更新、削除操作の合計)を達成する必要があり、50%のキャッシュ・ヒット率が予想される場合、各レプリケーション・ノードのI/Oサブシステムで次の値が達成される必要があります。
((2000 ops/sec) * (1 - 0.5)) / 6 nodes = 166 IOPs/sec
これは、おおむね単一スピンドル・ディスク・サブシステムで達成可能な範囲です。より高いスループットを得るには、複数スピンドルI/Oサブシステムの方が適切です。別のオプションは、シャードの数を増やすことで、その結果、レプリケーション・ノードの数が増え、ディスクの数が増えて、I/Oの負荷が分散されます。
ビッグ・データ・アプリケーションで使用されるJEキャッシュの適切なサイズを特定するには、com.sleepycat.je.util.DbCacheSizeユーティリティを使用します。このユーティリティを使用するには、レコードの数とキーのサイズを指定する必要があります。必要に応じて、予想されるデータのサイズなどの他の情報を指定することもできます。ユーティリティによって、短い表形式の情報が表示されます。必要な数値は、Minimum, internal nodes only行のCache Size列に示されます。
たとえば、平均キー・サイズが12バイトで平均値サイズが1000バイトの1億個のレコードを含む環境のJEキャッシュ・サイズを決定する場合、次のようにDbCacheSizeを起動します。
java -d64 -XX:+UseCompressedOops -jar je.jar DbCacheSize \
-key 12 -data 1000 -records 100000000
=== Environment Cache Overhead ===
3,156,253 minimum bytes
To account for JE daemon operation and record locks,
a significantly larger amount is needed in practice.
=== Database Cache Size ===
Minimum Bytes Maximum Bytes Description
--------------- --------------- -----------
2,888,145,968 3,469,963,312 Internal nodes only
107,499,427,952 108,081,245,296 Internal nodes and leaf nodes
=== Internal Node Usage by Btree Level ===
Minimum Bytes Maximum Bytes Nodes Level
--------------- --------------- ---------- -----
2,849,439,456 3,424,720,608 1,123,596 1
38,275,968 44,739,456 12,624 2
427,512 499,704 141 3
3,032 3,544 1 4
必要な数値は、出力のDatabase Cache Sizeセクションにあります。Minimum Bytes列には、内部ノードのみ用と、内部ノードおよびリーフ・ノード用の2つの数値があります。これは、このサイズのデータセットに使用する絶対最小キャッシュ・サイズは2.9 GBということを意味しています。ただし、格納されるのは内部データベース構造のみです。キャッシュはデータを保持できるほど大きくありません。
出力の2番目の数値は、データベース全体(すべてのデータを含む)の保持に必要な最小キャッシュ・サイズを表します。107.5 GBでは、それと同様のRAMを備えるマシンはほとんど存在しません。これは、データに関してある決定をここで下す必要があることを意味しています。具体的には、作業セットの大きさを決定する必要があります。作業セットとは、アプリケーションが頻繁にアクセスするため、メモリー内キャッシュに置いておいた方がよいデータのことです。作業セットの適切な大きさは、アプリケーションの性質によって決まります。望ましいのは、作業セットがノード・マシンで使用可能なRAM容量に十分適合するほどの小ささであることです。大量のディスクI/Oを避けることで最良の読取りスループットが実現するためです。
java -d64 -XX:+UseCompressedOops -jar je.jar DbCacheSize \
-key 12 -data 1000 -records 10000000
=== Environment Cache Overhead ===
3,156,253 minimum bytes
To account for JE daemon operation and record locks,
a significantly larger amount is needed in practice.
=== Database Cache Size ===
Minimum Bytes Maximum Bytes Description
--------------- --------------- -----------
288,816,824 346,998,968 Internal nodes only
10,749,982,264 10,808,164,408 Internal nodes and leaf nodes
=== Internal Node Usage by Btree Level ===
Minimum Bytes Maximum Bytes Nodes Level
--------------- --------------- ---------- -----
284,944,960 342,473,280 112,360 1
3,826,384 4,472,528 1,262 2
42,448 49,616 14 3
3,032 3,544 1 4
当然のことながら、現在のキャッシュ・サイズは、全データ・セット・サイズに対するサイズの約10%となっています(作業セットは全データ・セット・サイズの約10%であると判断されたためです)。つまり、サイズが約10.8 GBのキャッシュに作業セットを置くことができます。これは今日の商用ハードウェアでは容易に対応可能です。
DbCacheSizeユーティリティの使用法の詳細は、Javadocページ(http://docs.oracle.com/cd/E17277_02/html/java/com/sleepycat/je/util/DbCacheSize.html)を参照してください。このユーティリティを使用するには、<KVHOME>/lib/je.jarファイルをJavaクラスパスに追加する必要があることに注意してください。<KVHOME>は、Oracle NoSQL Databaseパッケージ・ファイルを配置したディレクトリを表します。
DbCacheSizeを使用してターゲットのキャッシュ・サイズ値を得たら、これをサポートするために必要なJavaヒープのサイズを求める必要があります。これを行うには、KVS Node Heap Shaping and Sizingスプレッドシートを使用します。DbCacheSizeで得た数値をスプレッドシートのセル8Bに入力します。そうすると、セル29Bに、推奨されるJavaヒープ・サイズが表示されます。
ファイル・システム・キャッシュは、システム・オーバーヘッドとJavaヒープ・サイズを除いた残りのメモリーになります。
KVS Node Heap Shaping and Sizingスプレッドシートは、Oracle NoSQL Databaseディストリビューションの<KVHOME>/doc/misc/MemoryConfigPlanning.xlsにあります。