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

前
 
次
 

20 キャッシュ内のデータの問合せ

Coherenceでは、現在キャッシュされている、一定の基準を満たすデータに対して問合せおよび索引付けを実行できます。問合せと索引は、Coherence付属のフィルタを採用した単純なものにするか、またはコレクションや配列などの複数値属性に対して実行できます。

問合せの概要

Coherenceでは、指定された一連の基準を満たすキャッシュ・エントリを検索できます。結果セットは必要に応じてソートできます。問合せは、コミット読取り分離を使用して評価されます。

問合せは、キャッシュされたデータにしか適用されないことに注意してください(CacheLoaderインタフェースを使用して、問合せに適合するデータが追加で取得されることもありません)。このため、問合せが実行される前にデータセット全体をキャッシュにロードする必要があります。データセットが大きすぎて使用可能なメモリーに収まらない場合は、特定のディメンション(日付など)でキャッシュ・コンテンツを制限し、問合せの構造に基づいてキャッシュ問合せとデータベース問合せの間を手動で切り替えることができます。保守性の面から、通常はキャッシュ対応のデータ・アクセス・オブジェクト(DAO)の内部にこの処理を実装するのが最適です。

索引を付けるには、各パーティション・キャッシュ・ノードで属性を抽出できることが必要です。専用のCacheServerインスタンスの場合は、これを実現するために(通常は)アプリケーション・クラスをCacheServerクラスパスにインストールする必要があります。

ローカル・キャッシュおよびレプリケーション・キャッシュについては、索引のないデータに対して問合せがローカルで評価されます。パーティション・キャッシュについては、索引を使用して(使用可能な場合)問合せがクラスタ全体で並列に実行されます。Coherenceにはコストベース・オプティマイザ(CBO)が搭載されています。索引のない属性にアクセスするには、オブジェクトをデシリアライズする必要があります(ただし、他の属性に索引を付けると、評価が必要なオブジェクトを減らすことができます)。

問合せの概念

問合せ処理の概念は、ValueExtractorインタフェースに基づいています。値エクストラクタは、問合せで指定のオブジェクトから属性を抽出するために使用されます(索引付けの場合も同様)。ほとんどの開発者に必要となるのは、このインタフェースのReflectionExtractor実装のみです。ReflectionExtractorは、リフレクションを使用してメソッド名(通常はgetName()}のようなgetterメソッド)を参照することにより、値オブジェクトから属性を抽出します。

ValueExtractor extractor = new ReflectionExtractor("getName");

toString()のようなObjectメソッドをはじめとする任意のvoid引数メソッドを使用できます(プロトタイプ作成/デバッグに有用)。索引には、従来のフィールド索引(オブジェクトのフィールドに付けられた索引)または機能別索引(仮想のオブジェクト属性に付けられた索引)を使用できます。たとえば、クラスにフィールド・アクセッサgetFirstNameおよびgetLastNameがある場合、そのクラスではこれらの名前を連結する関数getFullNameを定義し、その関数に索引を付けることができます。索引の詳細は、「問合せ索引の使用方法」を参照してください。

getName属性を持つオブジェクトが含まれているキャッシュに問合せを実行するには、Filterを使用する必要があります。フィルタには、指定したオブジェクトが基準を満たしているかどうかを判別するメソッドが1つあります。

例20-1 等価フィルタ

Filter filter = new EqualsFilter(extractor, "Bob Smith");

フィルタには、メソッド名を受け入れてReflectionExtractorを内部で構成する、便利なコンストラクタも含まれています。

例20-2 ReflectionExtractorを構築するフィルタ

Filter filter = new EqualsFilter("getName", "Bob Smith");

例20-3は、特定のフィルタ基準を満たすキャッシュ・エントリを選択するルーチンを示しています。

例20-3 フィルタ基準を満たすキャッシュ・エントリの選択

for (Iterator iter = cache.entrySet(filter).iterator(); iter.hasNext(); )
    {
    Map.Entry entry = (Map.Entry)iter.next();
    Integer key = (Integer)entry.getKey();
    Person person = (Person)entry.getValue();
    System.out.println("key=" + key + " person=" + person);
    }

例20-4は、フィルタを使用してキャッシュ・エントリを選択およびソートする方法を示しています。

例20-4 フィルタ基準を満たすキャッシュ・エントリの選択およびソート

// entrySet(Filter filter, Comparator comparator) 
Iterator iter = cache.entrySet(filter, null).iterator();

追加したnull引数は、キャッシュ内のComparableオブジェクトの自然な順序を使用して結果セットをソートする必要があることを指定します。クライアントでは、Comparatorの実装を用意することによって、結果セットの順序を明示的に指定する場合があります。ソート処理を行う場合は、ソートの前に結果セット全体を取得しておく必要があるために、Coherenceで適用できる最適化が著しく制限されるので注意してください。

単純な問合せの実行

例20-5は、単純な問合せを作成してGreaterEqualsFilterフィルタを使用する方法を示しています。

例20-5 フィルタを指定したキャッシュの問合せ

Filter filter = new GreaterEqualsFilter("getAge", 18);

for (Iterator iter = cache.entrySet(filter).iterator(); iter.hasNext(); )
    {
    Map.Entry entry = (Map.Entry) iter.next();
    Integer key = (Integer) entry.getKey();
    Person person = (Person) entry.getValue();
    System.out.println("key=" + key + " person=" + person);
    }

Coherenceでは、com.tangosol.util.filterパッケージに幅広いフィルタが用意されています。


注意:

  • パーティション・キャッシュでは、Coherence Enterprise Edition以降でのみ使用可能なパラレル問合せ機能を使用して、このメソッドを実装します。Coherence Standard Editionでパーティション・キャッシュを処理する場合、このメソッドは、クライアントに設定したデータを取得して処理します。

  • ニア・キャッシュを通じて問合せを実行することは可能ですが、問合せではニア・キャッシュのフロント部分が使用されません。問合せでニア・キャッシュを使用する場合は、次のシーケンスを使用するのが最適のアプローチです。

    Set setKeys = cache.keySet(filter);
    Map mapResult = cache.getAll(setKeys);
    

問合せ索引の使用方法

問合せ索引によって、値(またはそれらの値の属性)と対応するキーをQueryMap内に関連付けて問合せのパフォーマンスを向上させることができます。索引は、Coherence Enterprise Edition以上に備わる機能です。

この項は、次のトピックで構成されています。

索引の作成

QueryMapクラスのaddIndexメソッドを使用して、索引を作成します。問合せ可能な属性であれば、このメソッドを使用して索引を付けることができます。このメソッドには、次の3つのパラメータが含まれています。

addIndex(ValueExtractor extractor, boolean fOrdered, Comparator comparator)

例20-6は、索引の作成方法を示しています。

例20-6 索引を作成するサンプル・コード

NamedCache cache = CacheFactory.getCache("MyCache");
ValueExtractor extractor = new ReflectionExtractor("getAttribute");
cache.addIndex(extractor, true, null);

fOrdered引数では、索引構造をソートするかどうかを指定します。ソートされた索引は、「2つの日付の間に収まるエントリをすべて選択」や「姓がSで始まる従業員をすべて選択」などの範囲問合せに役立ちます。等価問合せの場合は、順序付けられていない索引を使用できます。この索引を使用すると、領域と時間の面で効率が向上することがあります。

comparator引数を使用すると、索引の順序付け用にカスタムのjava.util.Comparatorを指定できます。

addIndexメソッドは、単にキャッシュ実装のヒントとして示したものであり、索引がサポートされていない場合や目的の索引(またはそれに類似した索引)がすでに存在する場合は、キャッシュによって無視されることがあります。アプリケーションでは、確実に索引が提示されるようにするために、索引がすでに存在する場合でもこのメソッドをコールして索引を提示することが求められます。たとえば、分散環境では、各サーバーが起動時に同じ索引セットを推奨する可能性が高いため、別のサーバーが同じ索引をリクエスト済かどうかにかかわらず、これらの索引を無条件にリクエストしたとしても、アプリケーションにとってマイナス面はありません。

Coherenceでは必要に応じて問合せを結合することができます。またCoherenceには、索引の使用優先順位を決めるコストベース・オプティマイザ(CBO)が搭載されています。索引を利用するには、問合せで使用されているものと等価のエクストラクタ((Object.equals())を使用する必要があります。

適用された索引のリストは、JMXを使用してStorageManagerMBeanから取得できます。詳細は、第34章「JMXを使用したCoherenceの管理方法」を参照してください。

ユーザー定義索引の作成

アプリケーションを選択してユーザー定義索引を作成し、索引に追加されるエントリを管理できます。ユーザー定義索引は、通常、索引の保守に必要なメモリーや処理のオーバーヘッドの削減に使用されます。ユーザー定義索引を作成するには、アプリケーションでMapIndexインタフェースとIndexAwareExtractorインタフェースを実装する必要があります。この項では、条件付き索引を作成するためのインタフェースの実装を提供するConditionalIndexクラスとConditionalExtractorクラスについても説明します。条件付き索引は関連付けられたフィルタを使用して、エントリに索引を付ける必要があるかどうかを評価します。

MapIndexインタフェースの実装

MapIndexインタフェースは、索引付けされたMap(またはその値の属性)に格納されている値を、索引付けされたMapの対応キーに関連付けるために使用されます。アプリケーションは、このインタフェースを実装してカスタム索引を提供します。

次の例の実装では、null以外の値を持つエントリを追加するのみの索引が定義されています。これは、大量のエントリを持つキャッシュがあり、小さなサブセットにのみ意味のあるnull以外の値が格納されている場合に特に便利です。

public class CustomMapIndex implements MapIndex
   {
   public void insert(Map.Entry entry)
       {
       if (entry.getValue()!= null)
           {
           ...
           }
       }
   ...
   }

この例では、エントリの値を抽出する前にその値がnullかどうかが確認されますが、これは後でも実行できます。エントリの値がnullの場合、索引には何も挿入されません。nullに関する類似の確認は、MapIndex updateメソッドでも必要です。残りのMapIndexメソッドも、適切に実装される必要があります。

IndexAwareExtractorインタフェースの実装

IndexAwareExtractorインタフェースは、MapIndex索引の作成と破棄をサポートするValueExtractorインタフェースの拡張機能です。このインタフェースのインスタンスは、QueryMap APIとともに使用してカスタム索引の作成をサポートすることを目的としたものです。次の例は、このインタフェースの実装方法を示しており、先程作成したCustomMapIndexクラスの例に対するものです。

public class CustomIndexAwareExtractor
        implements IndexAwareExtractor, ExternalizableLite, PortableObject
    {
    public CustomIndexAwareExtractor(ValueExtractor extractor)
        {
        m_extractor = extractor;
        }
 
    public MapIndex createIndex(boolean fOrdered, Comparator comparator,
            Map mapIndex)
        {
        ValueExtractor extractor = m_extractor;
        MapIndex       index     = (MapIndex) mapIndex.get(extractor);
 
        if (index != null)
            {
            throw new IllegalArgumentException(
                    "Repetitive addIndex call for " + this);
            }
 
        index = new CustomMapIndex(extractor, fOrdered, comparator);
        mapIndex.put(extractor, index);
        return index;
        }
 
    public MapIndex destroyIndex(Map mapIndex)
        {
        return (MapIndex) mapIndex.remove(m_extractor);
        }
    ...
    }

この例では、基礎となるエクストラクタを実際に使用して索引を作成し、最終的にキャッシュ・エントリから値を抽出しています。IndexAwareExtractor実装は、既存のQueryMapインタフェースを保持しながら、カスタムのMapIndex実装の作成と破棄を管理するために使用されます。

IndexAwareExtractorは、QueryMap.addIndexコールとQueryMap.removeIndexコールに渡されます。これに対して、CoherenceはIndexAwareExtractorcreateIndexdestroyIndexをコールします。IndexAwareExtractorは、createIndexdestroyIndexに渡される、エクストラクタと索引を関連付けるMapを保持します。

条件付き索引の使用方法

条件付き索引はカスタム索引であり、上述のとおりMapIndexインタフェースとIndexAwareExtractorインタフェースの両方を実装し、関連付けられたフィルタを使用してエントリに索引を付ける必要があるかどうかを評価します。エントリの抽出された値は、そのフィルタがtrueに評価されている場合、索引に追加されます。実装クラスは、それぞれConditionalIndexConditionalExtractorです。

ConditionalIndexConditionalExtractorによって作成されます。ConditionalIndexで使用されるフィルタとエクストラクタは、ConditionalExtractorで設定され、QueryMap.addIndexコールの間にConditionalIndexコンストラクタに渡されます。

ConditionalExtractorは、ConditionalIndexの作成のみに使用されるIndexAwareExtractor実装です。基礎となるValueExtractorは、索引の作成時の値の抽出に使用され、指定された索引マップで作成されたConditionalIndexに関連付けられているエクストラクタです。値を抽出するConditionalExtractorの使用はサポートされていません。例:

ValueExtractor extractor = new ReflectionExtractor("getLastName");
Filter filter = new NotEqualsFilter("getId", null);
ValueExtractor condExtractor = new ConditionalExtractor(filter, extractor, true);
 
// add the conditional index which should only contain the last name values for the
// entries with non-null Ids
cache.addIndex(condExtractor, true, null);

問合せとメモリーの使用のバッチ

問合せを発行するクライアント上にメモリーを保持するために、様々なテクニックを使用して問合せ結果をバッチで取得することができます。

keySet形式の問合せをgetAll()と組み合せると、エントリ・セット全体がクライアント上ですぐにデシリアライズされないため、メモリーの消費が削減されます。また、ニア・キャッシュも利用されます。

例20-7 keySet問合せ形式の使用

// keySet(Filter filter)
Set setKeys = cache.keySet(filter);
Set setPageKeys = new HashSet();
int PAGE_SIZE = 100;
for (Iterator iter = setKeys.iterator(); iter.hasNext();)
    {
    setPageKeys.add(iter.next());
    if (setKeyPage.size() == PAGE_SIZE || !iter.hasNext())
        {
        // get a block of values
        Map mapResult = cache.getAll(setPageKeys);

        // process the block
        // ...

        setPageKeys.clear();
        }
    }

LimitFilterを使用すると、クライアントに送信するデータ量が制限されると同時に、ページングが使用可能になります。例20-8は、LimitFilterの使用方法を示しています。

例20-8 制限フィルタの使用

int pageSize = 25;
Filter filter = new GreaterEqualsFilter("getAge", 18);
// get entries 1-25
Filter limitFilter = new LimitFilter(filter, pageSize);
Set entries = cache.entrySet(limitFilter);

// get entries 26-50
limitFilter.nextPage();
entries = cache.entrySet(limitFilter);

分散/パーティション・キャッシュを使用するときには、問合せでPartitionedFilterを使用してパーティションとキャッシュ・サーバーをターゲットにすることができます。各問合せのリクエストは単一のキャッシュ・サーバーをターゲットとしているため、これは、問合せ結果をバッチ処理するための最も効率的な方法であり、リクエストに応答する必要のあるサーバーの数を減らし、ネットワークを最も効率的に使用できるようにします。


注意:

PartitionedFilterの使用はクラスタ・メンバーに制限されています。Coherence*Extendのクライアントは使用できません。Coherence*Extendクライアントは、上述の2つのテクニックのいずれかを使用できます。また、これらの問合せをInvocableとして実装し、リモートでCoherence*Extendクライアントが実行することも可能です。

パーティションで問合せパーティションを実行するには:

DistributedCacheService service =
   (DistributedCacheService) cache.getCacheService();
int cPartitions = service.getPartitionCount();
 
PartitionSet parts = new PartitionSet(cPartitions);
for (int iPartition = 0; iPartition < cPartitions; iPartition++)
    {
    parts.add(iPartition);
    Filter filterPart = new PartitionedFilter(filter, parts);
    Set setEntriesPart = cache.entrySet(filterPart);
 
    // process the entries ...
    parts.remove(iPartition);
    }

問合せは、サーバー・ベースでサーバー上で実行することもできます。

DistributedCacheService service =
   (DistributedCacheService) cache.getCacheService();
int cPartitions = service.getPartitionCount();
 
PartitionSet partsProcessed = new PartitionSet(cPartitions);
for (Iterator iter = service.getStorageEnabledMembers().iterator();
        iter.hasNext();)
    {
    Member member = (Member) iter.next();
    PartitionSet partsMember = service.getOwnedPartitions(member);
 
    // due to a redistribution some partitions may have already been processed
    partsMember.remove(partsProcessed);
    Filter filterPart = new PartitionedFilter(filter, partsMember);
    Set setEntriesPart = cache.entrySet(filterPart);
 
    // process the entries ...
    partsProcessed.add(partsMember);
    }
 
// due to a possible redistribution, some partitions may have been skipped
if (!partsProcessed.isFull())
    {
    partsProcessed.invert();
    Filter filter = new PartitionedFilter(filter, partsProcessed);
 
    // process the remaining entries ...
    }

複数値属性が関係する問合せ

Coherenceでは、コレクションや配列を含めた複数値属性の索引付けおよび問合せがサポートされています。オブジェクトに索引を付けるときには、そのオブジェクトが複数値タイプかどうかがチェックされてから、シングルトンでなくコレクションとして索引が付けられます。このようなコレクションに対する問合せには、ContainsAllFilterContainsAnyFilterおよびContainsFilterが使用されます。

例20-9 複数値属性の問合せ

Set searchTerms = new HashSet();
searchTerms.add("java");
searchTerms.add("clustering");
searchTerms.add("books");

// The cache contains instances of a class "Document" which has a method
// "getWords" which returns a Collection<String> containing the set of
// words that appear in the document.
Filter filter = new ContainsAllFilter("getWords", searchTerms);

Set entrySet = cache.entrySet(filter);

// iterate through the search results
// ...

ChainedExtractor

ChainedExtractorを実装すると、ゼロ引数(アクセッサ)メソッドの連鎖起動が可能になります。例20-10では、まずエクストラクタがリフレクションを使用して、キャッシュされているPersonオブジェクトごとにgetName()をコールします。その後、リフレクションを使用して、返されるStringlength()をコールします。

例20-10 連鎖起動メソッド

ValueExtractor extractor = new ChainedExtractor("getName.length");

このエクストラクタを問合せに渡し、(たとえば)名前が10文字以内の人をすべて、問合せで選択することができます。メソッドの起動は、getName.trim.lengthのように無限に連鎖できます。