多くの場合、Coherenceの分散キャッシュは既存のローカル・キャッシュと比較して評価されます。ローカル・キャッシュは一般的に、インプロセス・ハッシュ・マップの形式になります。Coherenceはインプロセスの非クラスタ・キャッシュに対応する機能を備えていますが、ローカル・キャッシュと分散キャッシュのパフォーマンスを直接比較するのは現実的ではありません。分散キャッシュはプロセス外という特性があるため、シリアライズとネットワーク転送を実行する必要があります。これと引き換えに、キャッシュ・データのクラスタ全体における一貫性や、単一のJVMまたはコンピュータで提供されるものを超えるデータと問合せのスケーラビリティが得られます。これは、分散キャッシュの使用で非常に優れたパフォーマンスを実現することが不可能なのではなく、適切なコンテキストでの評価が必要という意味です。
この章には次の項が含まれます:
パフォーマンスの評価時には、待機時間とスループットの2つについて確認します。単純なパフォーマンス分析テストでは、タイトなループで、一連の時限式キャッシュ・アクセスを実行しようとする場合があります。このテストでは、待機時間は正確に測定できますが、分散キャッシュの最大スループットを測定するには、キャッシュに同時にアクセスする複数のスレッドをテストで利用する必要があり、可能であれば複数のテスト・クライアントも利用します。単一のスレッドによるテストでは、クライアント・スレッドは当然、所要時間の大半をネットワークでの単純な待機に費やします。複数のクライアントとスレッドを実行すると、複数のリクエストを並列的に発行することによって、使用可能な処理能力をより効率的に利用できます。各処理のデータ密度を向上させるには、バッチ処理を使用することもできます。スレッドを追加すると、CPUまたはネットワークの利用が最大限度に達するまで、その期間の全体的な待機時間が一定に保たれ、スループットが引き続き向上することがわかります。
クラスタ・サイズの拡大に応じた真に直線的なスケーラビリティを示すには、単にJVMをクラスタに追加するのみでなくハードウェアの追加も準備します。JVMを1つのコンピュータに追加することで、CPUまたはネットワークが十分に使用されるポイントまでスケール・アップします。
3台以上のキャッシュ・サーバー(記憶域が有効なノード)を使用したクラスタのテストを計画します。キャッシュ・サーバーを1台から2台に拡張した場合と2台から4台に拡張した場合とでは、示されるスケーラビリティが異なります。その理由は、デフォルトのCoherenceでは、キャッシュに書き込まれたデータのそれぞれに対してバックアップ・コピーが1つ保持されるためです。バックアップの保持プロセスは、記憶域が有効なノードがクラスタ内に2つ存在する場合にのみ開始されます(バックアップを配置する場所が必要です)。このため、1台から2台に移行すると、put
操作のように変化が生じる操作に要する作業量が実際に2倍になりますが、3台以上になると作業量は変動せず、ノード全体に均等に分散されます。「スケーラビリティ: テスト・ケース」では、スケーラビリティを実証するテスト・ケースの例が示されています。
クラスタを最大限に活用するには、環境およびJVMをチューニングしておくことが重要です。第6章「パフォーマンス・チューニング」は、環境を最大限に利用するための良い手引きとなります。たとえば、CoherenceにはWindows用のレジストリ・スクリプト(optimize.reg
)があり、これによっていくつかの重要な設定が調整され、Windowsのデータ速度を大幅に上げることができます。
次のグラフは、100台のコンピュータで構成される環境における1つのクラスタのスケール・アウトの結果を示しています。この環境でCoherenceは、ネットワークの切替えインフラストラクチャの限度までスケーリングできました。ここでは、12台までのコンピュータで構成されたセットが8つあり、それぞれのセットには中央のスイッチに対して4GBのリンクが設定されました。したがってこのテストでは、Coherenceのネットワーク・スループットが、使用可能な帯域幅の限度となる32台のコンピュータまでスケール・アップし、33台以上になると、合計のデータ容量において引き続きスケーリングされています。
ペイロードが次のペイロード・サイズの1000倍であるため、10MBの操作の待機時間(300ミリ秒まで)は、表示上の理由によりグラフに含まれていません。
この項では、データ・グリッドの集計の例を使用して、Coherenceのスケーラビリティを実証します。ここでのテストは、Dual 2.3GHz PowerPC G5 Xserveコンピュータで構成されるクラスタで行われました。
この項には、次のトピックが含まれます:
Coherenceでは、クラスタ内で記憶域が有効なすべてのノードでデータセットを動的、透過的および自動的にパーティション化することによって、データ・グリッドが提供されます。InvocableMap
インタフェースでは、データ・グリッド(つまりパーティション・グリッド)の概念とグリッドに格納されるデータの処理が緊密にバインドされます。
次のテスト・ケースでは、あらゆるSLA要件とスループット要件の追加を処理するためにスケールアウト可能なアプリケーションをCoherenceで構築できることが示されています。このテスト・ケースの例では、Coherenceによって、1秒ごとに集計される取引数がハードウェアの増加応じて直線的に増加できることが明確に実証されています。つまり、あるコンピュータで1秒当たりX倍の取引数を集計できる場合は、データ・グリッドに追加された2台目コンピュータではその集計が1秒当たり2倍の取引数に増加し、データ・グリッドに追加された3台目のコンピュータではその集計が1秒当たり3倍の取引数に増加(これ以降も同様)します。
例3-1は、スケーラビリティ・テストの基礎として使用されるTrade
オブジェクトを示しています。Trade
オブジェクトには、Id
、Price
およびSymbol
の3つのプロパティがあります。集計されるデータがこれらのプロパティで示されます。
例3-1 3つのプロパティを定義するTradeオブジェクト
package com.tangosol.examples.coherence.data; import com.tangosol.util.Base; import com.tangosol.util.ExternalizableHelper; import com.tangosol.io.ExternalizableLite; import java.io.IOException; import java.io.NotActiveException; import java.io.DataInput; import java.io.DataOutput; /** * Example Trade class * * @author erm 2005.12.27 */ public class Trade extends Base implements ExternalizableLite { /** * Default Constructor */ public Trade() { } public Trade(int iId, double dPrice, String sSymbol) { setId(iId); setPrice(dPrice); setSymbol(sSymbol); } public int getId() { return m_iId; } public void setId(int iId) { m_iId = iId; } public double getPrice() { return m_dPrice; } public void setPrice(double dPrice) { m_dPrice = dPrice; } public String getSymbol() { return m_sSymbol; } public void setSymbol(String sSymbol) { m_sSymbol = sSymbol; } /** * Restore the contents of this object by loading the object's state from the * passed DataInput object. * * @param in the DataInput stream to read data from to restore the * state of this object * * @throws IOException if an I/O exception occurs * @throws NotActiveException if the object is not in its initial state, and * therefore cannot be deserialized into */ public void readExternal(DataInput in) throws IOException { m_iId = ExternalizableHelper.readInt(in); m_dPrice = in.readDouble(); m_sSymbol = ExternalizableHelper.readSafeUTF(in); } /** * Save the contents of this object by storing the object's state into the * passed DataOutput object. * * @param out the DataOutput stream to write the state of this object to * * @throws IOException if an I/O exception occurs */ public void writeExternal(DataOutput out) throws IOException { ExternalizableHelper.writeInt(out, m_iId); out.writeDouble(m_dPrice); ExternalizableHelper.writeSafeUTF(out, m_sSymbol); } private int m_iId; private double m_dPrice; private String m_sSymbol; }
例3-2では、容量が無制限の分散スキームに次のマップをマッピングするワイルドカード・キャッシュが定義されています。
例3-2 容量が無制限のcaching-schemeへのcache-mappingのマッピング
<?xml version="1.0"?> <cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd"> <caching-scheme-mapping> <cache-mapping> <cache-name>*</cache-name> <scheme-name>example-distributed</scheme-name> </cache-mapping> </caching-scheme-mapping> <caching-schemes> <!-- Distributed caching scheme. --> <distributed-scheme> <scheme-name>example-distributed</scheme-name> <service-name>DistributedCache</service-name> <backing-map-scheme> <class-scheme> <scheme-ref>unlimited-backing-map</scheme-ref> </class-scheme> </backing-map-scheme> <autostart>true</autostart> </distributed-scheme> <!-- Backing map scheme definition used by all the caches that do not require any eviction policies --> <class-scheme> <scheme-name>unlimited-backing-map</scheme-name> <class-name>com.tangosol.util.SafeHashMap</class-name> </class-scheme> </caching-schemes> </cache-config>
例3-3は、index
をPrice
プロパティに追加するコードを示しています。このプロパティに索引を追加すると、Coherenceから値に直接アクセスできるようになり、計算を実行するために各アイテムをデシリアライズする必要がなくなるため、パフォーマンスが向上します。
例3-3 Priceプロパティへの索引の追加
ReflectionExtractor extPrice = new ReflectionExtractor("getPrice"); m_cache.addIndex(extPrice, true, null);
このテスト・ケースでは、索引の適用後、集計速度が2倍以上増加しました。
例3-4は、データ・グリッド内のすべてのJVMで並列集計を実行するコードを示しています。集計は単一のクライアントによって開始され、このクライアントに結果が返されます。つまり、処理能力の低い単一のクライアントで、わずか1行のコードにより、クラスタ/データ・グリッドのすべての集計処理能力を使用して、この集計を並列的に実行できます。
この項では、テストの実行例と全体のテスト環境について説明します。クライアントとサーバーで使用したJDKは、どちらもJava 2 Runtime Environment, Standard Edition(ビルド1.5.0_05-84)です。
テスト実行では次の処理を行います。
200,000件のTradeオブジェクトをデータ・グリッドにロードします。
Price
プロパティに索引を追加します。
データ・グリッドに格納されているすべてのTradeオブジェクトの並列集計を実行します。この集計手順を20回実行して平均実行時間を求め、テストでガベージ・コレクションが考慮されるようにします。
400,000件のTradeオブジェクトをデータ・グリッドにロードします。
ステップ2と3を繰り返します。
600,000件のTradeオブジェクトをデータ・グリッドにロードします。
ステップ2と3を繰り返します。
800,000件のTradeオブジェクトをデータ・グリッドにロードします。
ステップ2と3を繰り返します。
1,000,000件のTradeオブジェクトをデータ・グリッドにロードします。
ステップ2と3を繰り返します。
クライアントに関する考慮事項: テスト・クライアント自体の実行場所は、ローカル記憶域が無効としてマークされたIntel Core Duo iMacです。クライアントの起動に使用したコマンドラインは次のとおりです。
java ... -Dtangosol.coherence.distributed.localstorage=false -Xmx128m -Xms128m com.tangosol.examples.coherence.invocable.TradeTest
このテスト・スイート(および後続の結果)は、次の4回のテスト実行のデータが含まれています。
1台のXserveで4つのJVMを起動し、テスト実行を開始します。
2台のXserveでそれぞれで4つのJVMを起動し、テスト実行を開始します。
3台のXserveでそれぞれで4つのJVMを起動し、テスト実行を開始します。
4台のXserveでそれぞれで4つのJVMを起動し、テスト実行を開始します。
サーバーに関する考慮事項: この場合のJVMは、データの管理/格納を担当するキャッシュ・サーバー(DefaultCacheServer
)インスタンスを表します。
サーバーの起動には、次のコマンドラインを使用しました。
java ... -Xmx384m -Xms384m -server com.tangosol.net.DefaultCacheServer
次のグラフでは、データ・グリッドにキャッシュ・サーバー/コンピュータを追加するたびに集計の平均集計時間が直線的に短縮されることを示しています。
注意: 次に示す計算では最小時間と最大時間は使用しておらず、18回の結果のデータセットを基に平均値を算出しました。 |
同様に、次のグラフでは、コンピュータを追加するたびに1秒当たりの集計数が直線的に増加する様子を示しています。1台のコンピュータから2台のコンピュータに移行すると、1秒当たりで集計される取引数は2倍になり、2台のコンピュータから4台のコンピュータに移行すると、1秒当たりで集計される取引数はさらに2倍になります。
スケーリングに加えて、集計中に1つのキャッシュ・サーバーまたは1つのコンピュータ全体が故障した場合でも、前述の集計は正常かつ正確に完了します。