この章では、C++クライアントからCoherenceキャッシュの問合せを実行する手順を示します。
この章には次の項が含まれます:
Coherenceでは、現在キャッシュされているデータのうち、指定された一連の基準を満たすものに対して、問合せと索引を実行できます。問合せと索引は、Coherence付属のフィルタを採用した単純なものにするか、またはコレクションや配列などの複数値属性に対して実行できます。結果セットは必要に応じてソートできます。問合せは、コミット読取り分離を使用して評価されます。
問合せは、キャッシュされたデータにしか適用されないことに注意してください(CacheLoader
インタフェースを使用して、問合せに適合するデータが追加で取得されることもありません)。このため、問合せが実行される前にデータセット全体をキャッシュにロードする必要があります。データセットが大きすぎて使用可能なメモリーに収まらない場合は、特定のディメンション(日付など)に従ってキャッシュの内容を制限し、問合せの構造に基づいてキャッシュ問合せとデータベース問合せを手動で切り替えることができます。保守性の面から、通常はキャッシュ対応のデータ・アクセス・オブジェクト(DAO)の内部にこの処理を実装するのが最適です。
索引を付けるには、各パーティション・キャッシュ・ノードで属性を抽出できることが必要です。専用のCacheServer
インスタンスの場合は、これを実現するために(通常は)アプリケーション・クラスをCacheServer
クラスパスにインストールする必要があります。
ローカル・キャッシュおよびレプリケーション・キャッシュについては、索引のないデータに対して問合せがローカルで評価されます。パーティション・キャッシュについては、索引を使用して(使用可能な場合)問合せがクラスタ全体で並列に実行されます。Coherenceにはコストベース・オプティマイザ(CBO)が搭載されています。索引のない属性にアクセスするには、オブジェクトをデシリアライズする必要があります(ただし、他の属性に索引を付けると、評価が必要なオブジェクトを減らすことができます)。
キャッシュの内容を問い合せる操作は、例11-1に示すように非常に単純です。
例11-1 キャッシュの内容の問合せ
ValueExtractor::Handle hExtractor = ReflectionExtractor::create("getAge"); Filter::View vFilter = GreaterEqualsFilter::create(hExtractor, Integer32::valueOf(18)); for (Iterator::Handle hIter = hCache->entrySet(vFilter)->iterator(); hIter->hasNext(); ) { Map::Entry::Handle hEntry = cast<Map::Entry::Handle>(hIter->next()); Integer32::View vKey = cast<Integer32::View>(hEntry->getKey()); Person::Handle hPerson = cast<Person::Handle>(hEntry->getValue()); std::cout << "key=" << vKey << " person=" << hPerson; }
Coherenceでは、coherence::util::Filter
パッケージに多様なフィルタが用意されています。LimitFilter
を使用すると、クライアントに送信されるデータ量を制限できるほか、ユーザー側でページングを実現できます。
例11-2 LimitFilterメソッドの使用方法
int32_t nPageSize = 25; ValueExtractor::Handle hExtractor = ReflectionExtractor::create("getAge"); Filter::View vFilter = GreaterEqualsFilter::create(hExtractor, Integer32::valueOf(18)); // get entries 1-25 LimitFilter::Handle hLimitFilter = LimitFilter::create(vFilter, nPageSize); Set::View vEntries = hCache->entrySet(hLimitFilter); // get entries 26-50 hLimitFilter->nextPage(); vEntries = hCache->entrySet(hLimitFilter);
問合せ可能な属性には、addIndex
クラスのQueryMap
メソッドで索引を付けることができます。
例11-3 問合せ可能な属性に対する索引付け
// addIndex(ValueExtractor::View vExtractor, boolean_t fOrdered, Comparator::View vComparator) hCache->addIndex(hExtractor, true, NULL);
fOrdered
引数では、索引構造をソートするかどうかを指定します。ソートされた索引は、「2つの日付の間に収まるエントリをすべて選択」や「姓がSで始まる従業員をすべて選択」などの範囲問合せに役立ちます。等価問合せの場合は、順序付けられていない索引を使用できます。この索引を使用すると、領域と時間の面で効率が向上することがあります。
comparator引数は、索引の順序付け用にカスタムのjava.util.Comparator
を指定します。
注意: このメソッドは、単にキャッシュ実装のヒントとして示したものであり、索引がサポートされていない場合や目的の索引(またはそれに類似した索引)が存在する場合は、キャッシュによって無視されることがあります。アプリケーションでは、確実に索引が提示されるようにするために、索引が存在する場合でもこのメソッドをコールして索引を提示することが求められます。たとえば、分散環境では各サーバーが起動時に同じ索引セットを提示する可能性があり、別のサーバーが同じ索引を要求しているかどうかに関係なく、アプリケーションがそれらの索引を不必要に要求しても不都合は生じません。 |
Coherenceでは必要に応じて問合せを結合することができます。またCoherenceには、索引の使用優先順位を決めるコストベース・オプティマイザ(CBO)が搭載されています。索引を利用するには、問合せで使用されているエクストラクタと同等((Object->equals()
)のエクストラクタを、同じ問合せで使用する必要があります。
パーティション・キャッシュは、Parallel Query機能を使用してQueryMapインタフェースを実行するため、大きなデータセットでも高性能の問合せが可能です。
この項では、問合せインタフェースの設計について、コアなコンポーネントから順に詳しく説明していきます。
問合せ処理の概念は、ValueExtractor
インタフェースに基づいています。値エクストラクタは、問合せで指定のオブジェクトから属性を抽出するために使用されます(索引付けの場合も同様)。ほとんどの開発者に必要となるのは、このインタフェースのReflectionExtractor
実装のみです。ReflectionExtractorは、リフレクションを使用してメソッド名(通常はgetName()
のようなgetterメソッド)を参照することにより、値オブジェクトから属性を抽出します。
ReflectionExtractor::Handle hExtractor = ReflectionExtractor::create("getName");
toString()
のようなObject
メソッドをはじめとする任意のvoid引数メソッドを使用できます(プロトタイプ作成/デバッグに有用)。索引には、従来のフィールド索引(オブジェクトのフィールドに付けられた索引)または機能別索引(仮想のオブジェクト属性に付けられた索引)を使用できます。たとえば、クラスにフィールド・アクセッサgetFirstName
およびgetLastName
がある場合、そのクラスではこれらの名前を連結する関数getFullName
を定義し、その関数に索引を付けることができます。
getName
属性を持つオブジェクトが含まれているキャッシュに問合せを実行するには、Filter
を使用する必要があります。フィルタには、指定したオブジェクトが基準を満たしているかどうかを判別するメソッドが1つあります。
Filter::Handle hEqualsFilter = EqualsFilter::create(hExtractor, String::create("Bob Smith"));
特定のフィルタに適合するキャッシュ・エントリを選択する方法は次のとおりです。
例11-4 特定のフィルタに適合するキャッシュ・エントリの選択
for (Iterator::Handle hIter = hCache->entrySet(hEqualsFilter)->iterator(); hIter->hasNext(); ) { Map::Entry::Handle hEntry = cast<Map::Entry::Handle>(hIter->next()); Integer32::View vKey = cast<Integer32::View>(hEntry->getKey()); Person::Handle hPerson = cast<Person::Handle>(hEntry->getValue()); std::cout << "key=" << vKey << " person=" << hPerson; }
エントリを選択してソートする方法は次のとおりです。
例11-5 エントリの選択およびソート
// entrySet(Filter::View vFilter, Comparator::View vComparator) Iterator::Handle hIter = hCache->entrySet(hEqualsFilter, NULL)->iterator();
NULL引数を追加することで、キャッシュ内の比較可能なオブジェクトの自然な順序で結果セットをソートするように指定しています。クライアントでは、Comparatorの実装を提供することで、結果セットの順序を明示的に指定できます。ソート処理を行う場合は、ソートの前に結果セット全体を取得しておく必要があるために、Coherenceで適用できる最適化が著しく制限されるので注意してください。
keySet
形式の問合せをgetAll()
と組み合せることで、メモリー使用量をより細かく制御できます。
例11-6 keySet形式の問合せの使用
// keySet(Filter::View vFilter) Set::View vSetKeys = hCache->keySet(vFilter); Set::Handle hSetPageKeys = HashSet::create(); int32_t PAGE_SIZE = 100; for (Iterator::Handle hIter = vSetKeys->iterator(); hIter->hasNext();) { hSetPageKeys->add(hIter->next()); if (hSetPageKeys->size() == PAGE_SIZE || !hIter->hasNext()) { // get a block of values Map::View vMapResult = hCache->getAll(hSetPageKeys); // process the block // ... hSetPageKeys->clear(); } }
Coherenceでは、コレクションや配列を含めた複数値属性の索引付けおよび問合せがサポートされています。オブジェクトに索引を付けるときには、そのオブジェクトが複数値タイプかどうかがチェックされてから、シングルトンでなくコレクションとして索引が付けられます。このようなコレクションに対する問合せには、ContainsAllFilter
、ContainsAnyFilter
およびContainsFilter
が使用されます。
例11-7 複数値属性に対する索引付けおよび問合せ
Set::Handle hSearchTerms = HashSet::create(); hSearchTerms->add(String::create("java")); hSearchTerms->add(String::create("clustering")); hSearchTerms->add(String::create("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. ValueExtractor::Handle hExtractor = ReflectionExtractor::create("getWords"); Filter::View vFilter = ContainsAllFilter::create(hExtractor, hSearchTerms); Set::View vEntrySet = hCache->entrySet(vFilter); // iterate through the search results // ...
ChainedExtractor
実装を使用すると、ゼロ引数(アクセッサ)メソッドの連鎖起動が可能となります。例11-8では、エクストラクタはまず、キャッシュされている各Person
オブジェクトに対し、リフレクションを使用してgetName()
をコールし、返されたString
に対し、リフレクションを使用してlength()
をコールします。このエクストラクタを問合せに渡し、(たとえば)名前が10文字以内の人をすべて、問合せで選択することができます。
例11-8 ChainedExtractor実装の使用
ChainedExtractor::Handle hExtractor = ChainedExtractor::create(ChainedExtractor::createExtractors("getName.length"));
メソッドの起動は、getName.trim.length
のように、無限に連鎖させることができます。
QueryRecorder
クラスは指定されたフィルタの説明またはトレース・レコードを生成します。このクラスは、クラスタ内のすべてのノードを問い合せ、結果を集約する機能を持つ並列アグリゲータの実装です。このクラスでは2種類のレコードがサポートされています。その1つはQueryRecorder::explain
レコードで、問合せ処理の間に実行されたフィルタの評価の見積りコストを提供します。もう1つはQueryRecorder::trace
レコードで、問合せ処理の間に実行されたフィルタの評価の実際のコストを提供します。両方の問合せレコードでは、フィルタで索引が使用可能かどうかが考慮されます。EXPLAIN PLANレコードおよびトレース・レコードで提供されるデータの詳細は、『Oracle Coherenceでのアプリケーションの開発』を参照してください。
問合せレコードを作成するには、RecordType
パラメータを指定する新しいQueryRecorder
インスタンスを作成します。テストするインスタンスとフィルタをAggregate
メソッドのパラメータとして含めます。次の例では、説明レコードを作成しています。
NamedCache::Handle hCache = CacheFactory::getCache("MyCache"); IdentityExtractor::View hExtract = IdentityExtractor::getInstance(); OrFilter::Handle hFilter = OrFilter::create( GreaterEqualsFilter::create(hExtract, Integer32::create(50)), LessEqualsFilter::create(hExtract, Integer32::create(20))); QueryRecord::View vRecord = cast<QueryRecord::View>(hCache->aggregate( (Filter::View) hFilter, QueryRecorder::create(QueryRecorder::explain))); cout << vRecord;
トレース・レコードを作成するには、RecordType
パラメータをtrace
に変更します。
QueryRecord::View vRecord = cast<QueryRecord::View>(hCache->aggregate(
(Filter::View) hFilter, QueryRecorder::create(QueryRecorder::trace)));