Coherenceでは、クラスタ内で記憶域が有効なすべてのノードでデータセットを動的、透過的および自動的にパーティション化することによって、データ・グリッドが提供されます。弊社では、新たなDual 2.3GHz PowerPC G5 Xserveクラスタでスケールアウト・テストを実施しました。ここでは、データ・グリッド集計機能を使用して実行したテストの1つを示します。
新たなInvocableMapでは、データ・グリッド(つまりパーティション・グリッド)の概念とグリッドに格納されるデータの処理が緊密にバインドされます。InvocableMapをCoherence自体の線形スケーラビリティと組み合せると、きわめて強力なソリューションが得られます。次のテストは、Coherenceによって、開発者、エンジニア、アーキテクトなどが、あらゆるSLA要件、およびスループット要件のあらゆる増加に対処するためにスケールアウト可能なアプリケーションを構築できることを示しています。たとえば、次のテストは、ハードウェアの増加に伴い、1秒当たりで集計される取引数をCoherenceが線形的に増加できることを示したものです。つまり、1台のマシンで1秒当たりX件の取引を集計できる場合、2台目のマシンをデータ・グリッドに追加すると、1秒当たりで集計できる取引数は2Xになり、3台目のマシンをデータ・グリッドに追加すると1秒当たりで集計できる取引数は3Xになります(以下同様)。
次に示すデータ・グリッドの機能は、いずれもCoherence Enterprise Edition以上が備える機能です。
まず、集計するデータが必要です。例O-1は、Id、Price、Symbolの3つのプロパティを持つTradeオブジェクトを示しています。
例O-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;
}
キャッシュ・コンフィギュレーションは、XMLキャッシュ・コンフィギュレーションの要素を使用して簡単に設定できます。例O-2では、容量が無制限の1つのcaching-schemeへのマッピングを行う、1つのワイルドカードcache-mappingを定義しています。
例O-2 容量が無制限のcaching-schemeへのcache-mappingのマッピング
<?xml version="1.0"?>
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
<cache-config>
<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>
<init-params></init-params>
</class-scheme>
</caching-schemes>
</cache-config>
例O-3は、Priceプロパティにindexを追加するコードを示しています。このプロパティにindexを追加すると、Coherenceから値に直接アクセスできるようになり、計算を実行するために各アイテムをデシリアライズする必要がなくなるため、パフォーマンスが向上します。
例O-3 Priceプロパティへのindexの追加
ReflectionExtractor extPrice = new ReflectionExtractor("getPrice");
m_cache.addIndex(extPrice, true, null);
弊社のテストでは、indexの適用後、集計速度が2倍以上増加しました。
例O-4は、データ・グリッド内のすべてのJVMで並列集計を実行するコードを示しています。集計は単一のクライアントによって開始され、このクライアントに結果が返されます。つまり、処理能力の低い単一のクライアントで、わずか1行のコードにより、クラスタ/データ・グリッドのすべての集計処理能力を使用して、この集計を並列実行できます。
テスト実行では次の処理を行います。
200,000件のTradeオブジェクトをデータ・グリッドにロードします。
Priceプロパティにindexを追加します。
データ・グリッドに格納されているすべての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
1台のXserveで4つのJVMを起動し、テスト実行を開始します。
2台のXserveのそれぞれで4つのJVMを起動し、テスト実行を開始します。
3台のXserveのそれぞれで4つのJVMを起動し、テスト実行を開始します。
4台のXserveのそれぞれで4つのJVMを起動し、テスト実行を開始します。
サーバーに関する考慮事項: この場合、JVMは、データの管理/格納を行うスタンドアロンJVMであるキャッシュ・サーバー・インスタンス(つまり、データ・グリッド・ノード)を表します。この実行には、DefaultCacheServerヘルパー・クラスを使用しました。
サーバーの起動に使用したコマンドラインは次のとおりです。
java ... -Xmx384m -Xms384m -server com.tangosol.net.DefaultCacheServer
次のグラフでわかるように、集計に要した平均集計時間は、データ・グリッドにキャッシュ・サーバー/マシンを追加するごとに線形に短縮されます。
|
注意: 次に示す計算では最小時間と最大時間は使用しておらず、18回の結果のデータセットを基に平均値を算出しました。 |
同様に、次のグラフは、マシンを追加するごとに1秒当たりの集計数が線形に増加する様子を示しています。1台のマシンから2台のマシンに移行すると、1秒当たりで集計される取引数は2倍になり、2台のマシンから4台のマシンに移行すると、1秒当たりで集計される取引数はさらに2倍になります。
|
注意: フェイルオーバー前述の集計は、集計の実行中にキャッシュ・サーバーのいずれか、または1台のマシン全体に障害が発生した場合でも、正常かつ適切に終了します。 |