ヘッダーをスキップ
Oracle® Coherence開発者ガイド
リリース3.6.1
B61368-02
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次

前
 
次
 

48 データ・グリッド集計の線形スケールアウト

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以上が備える機能です。

データ

まず、集計するデータが必要です。例48-1は、IdPriceおよびSymbolの3つのプロパティを持つTradeオブジェクトを示しています。

例48-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;
    }

パーティション・キャッシュの構成

キャッシュ構成要素は、キャッシュの構成に使用します。例48-2では、容量が無制限の1つのcaching-schemeへのマッピングを行う、1つのワイルドカードcache-mappingを定義しています。

例48-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>

Priceプロパティへのindexの追加

例48-3は、indexPriceプロパティに追加するコードを示しています。このプロパティにindexを追加すると、Coherenceから値に直接アクセスできるようになり、計算を実行するために各アイテムをデシリアライズする必要がなくなるため、パフォーマンスが向上します。

例48-3 Priceプロパティへのindexの追加

ReflectionExtractor extPrice  = new ReflectionExtractor("getPrice");
m_cache.addIndex(extPrice, true, null);

弊社のテストでは、indexの適用後、集計速度が2倍以上増加しました。

並列集計を実行するコード

例48-4は、データ・グリッド内のすべてのJVMで並列集計を実行するコードを示しています。集計は単一のクライアントによって開始され、このクライアントに結果が返されます。つまり、処理能力の低い単一のクライアントで、わずか1行のコードにより、クラスタ/データ・グリッドのすべての集計処理能力を使用して、この集計を並列実行できます。

例48-4 グリッドにおけるすべてのJVMでの並列集計の実行

Double DResult;
DResult = (Double) m_cache.aggregate((Filter) null, new DoubleSum("getPrice"));

テスト環境およびテスト・プロセス

テスト実行

テスト実行では次の処理を行います。

  1. 200,000件のTradeオブジェクトをデータ・グリッドにロードします。

  2. Priceプロパティにindexを追加します。

  3. データ・グリッドに格納されているすべてのTradeオブジェクトの並列集計を実行します。この集計手順を20回実行して平均実行時間を求め、テストでガベージ・コレクションが考慮されるようにします。

  4. 400,000件のTradeオブジェクトをデータ・グリッドにロードします。

  5. ステップ2と3を繰り返します。

  6. 600,000件のTradeオブジェクトをデータ・グリッドにロードします。

  7. ステップ2と3を繰り返します。

  8. 800,000件のTradeオブジェクトをデータ・グリッドにロードします。

  9. ステップ2と3を繰り返します。

  10. 1,000,000件のTradeオブジェクトをデータ・グリッドにロードします。

  11. ステップ2と3を繰り返します。

クライアントに関する考慮事項: テスト・クライアント自体の実行場所は、ローカル記憶域が無効としてマークされたIntel Core Duo iMacです。クライアントの起動に使用したコマンドラインは次のとおりです。

java ... -Dtangosol.coherence.distributed.localstorage=false -Xmx128m -Xms128m com.tangosol.examples.coherence.invocable.TradeTest

このテスト・スイート(および後続の結果)は4回のテスト実行のデータを含有

  1. 1台のXserveで4つのJVMを起動し、テスト実行を開始します。

  2. 2台のXserveのそれぞれで4つのJVMを起動し、テスト実行を開始します。

  3. 3台のXserveのそれぞれで4つのJVMを起動し、テスト実行を開始します。

  4. 4台のXserveのそれぞれで4つのJVMを起動し、テスト実行を開始します。

サーバーに関する考慮事項: この場合、JVMは、データの管理/格納を行うスタンドアロンJVMであるキャッシュ・サーバー・インスタンス(つまり、データ・グリッド・ノード)を表します。この実行には、DefaultCacheServerヘルパー・クラスを使用しました。

サーバーの起動に使用したコマンドラインは次のとおりです。

java ... -Xmx384m -Xms384m -server com.tangosol.net.DefaultCacheServer

JDKのバージョン

クライアントとサーバーで使用したJDKは、どちらもJava 2 Runtime Environment, Standard Edition(ビルド1.5.0_05-84)です。

結果

次のグラフでわかるように、集計に要した平均集計時間は、データ・グリッドにキャッシュ・サーバー/マシンを追加するごとに線形に短縮されます。


注意:

次に示す計算では最小時間と最大時間は使用しておらず、18回の結果のデータセットを基に平均値を算出しました。

図48-1 平均集計時間

この図については本文で説明しています。

同様に、次のグラフは、マシンを追加するごとに1秒当たりの集計数が線形に増加する様子を示しています。1台のマシンから2台のマシンに移行すると、1秒当たりで集計される取引数は2倍になり、2台のマシンから4台のマシンに移行すると、1秒当たりで集計される取引数はさらに2倍になります。

図48-2 集計のスケールアウト

この図については本文で説明しています。

注意:

フェイルオーバー

前述の集計は、集計の実行中にキャッシュ・サーバーのいずれか、または1台のマシン全体に障害が発生した場合でも、正常かつ適切に完了します。


結論

Coherenceデータ・グリッド(つまり、パーティション・キャッシュ)とInvocableMap機能を組み合せると、次のことが可能になります。