22 キャッシュ内のデータの問合せ
この章の内容は次のとおりです。
- 問合せの概要
Coherenceでは、指定された一連の基準を満たすキャッシュ・エントリを検索できます。 - 問合せの概念
問合せ処理の概念は、ValueExtractor
インタフェースに基づいています。 - 問合せの実行
Coherenceでは、com.tangosol.util.filter
パッケージに多数の事前作成フィルタが含まれています。 - フィルタ結果の効率的な処理
大量のデータ・セットをバッチで問い合せても、ヒープ領域不足を防ぐことができます。 - 問合せ索引の使用
問合せ索引によって、値(またはそれらの値の属性)と対応するキーをQueryMap
内に関連付けて問合せのパフォーマンスを向上させることができます。 - バッチ問合せの実行
問合せを発行するクライアント上にメモリーを保持するために、問合せ結果をバッチで取得できる様々なテクニックがあります。 - 複数値属性での問合せの実行
Coherenceでは、コレクションや配列を含めた複数値属性の索引付けおよび問合せがサポートされています。 - 連鎖エクストラクタの使用
ChainedExtractor
実装を使用すると、ゼロ引数(アクセッサ)メソッドの連鎖起動が可能となります。 - 問合せ結果の一貫性チェックをスキップするオプション
Coherenceではデフォルトで、指定したフィルタに一致するエントリが問合せ結果に含まれます。ただし、ターゲットとなるパーティションが同時に変更された場合、一貫性チェックを実行すると、問合せの再評価が繰り返されることがあります。 - 問合せのコストと効果の評価
問合せ内の各フィルタの見積もりコストと実際の効果をそれぞれ表示するために、問合せの実行計画レコードおよび問合せトレース・レコードを作成できます。
親トピック: データ・グリッド操作の実行
問合せの概要
現在、問合せはキャッシュされたデータにのみ適用されます(CacheLoader
インタフェースを使用して、問合せに一致する可能性がある追加データが取得されることはありません)。このため、問合せが実行される前にデータセット全体をキャッシュにロードする必要があります。データセットが大きすぎて使用可能なメモリーに収まらない場合は、特定のディメンション(日付など)に従ってキャッシュの内容を制限し、問合せの構造に基づいてキャッシュ問合せとデータベース問合せを手動で切り替えることができます。保守性の面から、通常はキャッシュ対応のデータ・アクセス・オブジェクト(DAO)の内部にこの処理を実装するのが最適です。
索引を付けるには、各パーティション・キャッシュ・ノードで属性を抽出できることが必要です。専用のキャッシュ・サーバー・インスタンスの場合は、これを実現するために(通常は)アプリケーション・クラスをキャッシュ・サーバーのクラスパスにインストールする必要があります。
ローカル・キャッシュおよびレプリケート・キャッシュの場合は、索引なしのデータまたは索引付きのデータに対して問合せをローカルで評価できます。パーティション・キャッシュの場合、問合せは通常、クラスタ全体でパラレルに実行され、索引が使用されます。索引のない属性にアクセスするには、オブジェクトをデシリアライズする必要があります(ただし、他の属性に索引を付けると、評価が必要なオブジェクトを減らすことができます)。最後に、Coherenceにはコストベース・オプティマイザ(CBO)が用意されており、問合せの効率化を支援するトレースおよび実行レポートもサポートされます。
ノート:
フィルタ・インタフェースを実装するすべてのクラスは、オブジェクトのシリアル化可能な状態のみに基づくようにhashCode()
およびequals()
メソッドを明示的に実装する必要があります。これは、フィルタがシリアル化され、ネットワーク経由で転送され、最終的にマップ・キーとして使用される、ObservableMap.addMapListener
およびその他の類似の場所でフィルタを使用する場合に特に重要です。ValueExtractor実装でも同様に有効です。
親トピック: キャッシュ内のデータの問合せ
問合せの概念
ValueExtractor
インタフェースに基づいています。値エクストラクタは、指定されたオブジェクトから属性を抽出し、問合せ(および同様に索引付け)するために使用されます。ほとんどの開発者に必要なのは、このインタフェースのReflectionExtractor
実装のみです。実装は、リフレクションを使用してメソッド名(通常はgetterメソッド)を参照することにより、値オブジェクトから属性を抽出します。たとえば:
ValueExtractor extractor = new ReflectionExtractor("getName");
toString()
のようなObject
メソッドをはじめとする任意のvoid
引数メソッドを使用できます(プロトタイプ/デバッグに有用)。索引には、従来のフィールド索引(オブジェクトのフィールドに付けられた索引)または機能ベースの索引(仮想のオブジェクト属性に付けられた索引)を使用できます。たとえば、クラスにフィールド・アクセッサgetFirstName
およびgetLastName
がある場合、そのクラスではこれらの名前を連結する関数getFullName
を定義し、その関数に索引を付けることができます。問合せ索引の使用を参照してください。
getName
属性を持つオブジェクトが含まれているキャッシュに問合せを実行するには、Filter
を使用する必要があります。フィルタには、指定したオブジェクトが基準を満たしているかどうかを判別するメソッドが1つあります。
Filter filter = new EqualsFilter(extractor, "Bob Smith");
フィルタには、メソッド名を受け入れてReflectionExtractor
を内部で構成する、便利なコンストラクタも含まれています。
Filter filter = new EqualsFilter("getName", "Bob Smith");
次の例は、特定のフィルタ基準を満たすキャッシュ・エントリを選択するルーチンを示しています。
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); }
次の例では、キャッシュ・エントリを選択してソートするフィルタを使用します。
// entrySet(Filter filter, Comparator comparator) Iterator iter = cache.entrySet(filter, null).iterator();
追加したnull
引数は、キャッシュ内のComparable
オブジェクトの自然な順序を使用して結果セットをソートする必要があることを指定します。クライアントでは、Comparator
の実装を用意することによって、結果セットの順序を明示的に指定する場合があります。ソート処理を行う場合は、ソートの前に結果セット全体を取得しておく必要があるために、Coherenceで適用できる最適化が著しく制限されるので注意してください。
親トピック: キャッシュ内のデータの問合せ
問合せの実行
com.tangosol.util.filter
パッケージに多数の事前作成フィルタが含まれています。例22-1は、問合せを作成してGreaterEqualsFilter
フィルタを使用する方法を示しています。
例22-1 フィルタを指定したキャッシュの問合せ
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); }
ノート:
ニア・キャッシュを通じて問合せを実行することは可能ですが、問合せではニア・キャッシュのフロント部分が使用されません。問合せでニア・キャッシュを使用する場合は、次のシーケンスを使用するのが最適のアプローチです。
Set setKeys = cache.key set(filter); Map mapResult = cache.getAll(setKeys);
親トピック: キャッシュ内のデータの問合せ
フィルタ結果の効率的な処理
BUFFER_SIZE
(この例では100)のみです。
ノート:
LimitFilter
APIは、次の例と同様に、結果を別々に処理できます。ただし、LimitFilter
は、ユーザー・インタフェースのように結果がページングされるシナリオに適しています。問合せ結果のデータをすべて処理する場合には効率的な方法ではありません。
例22-2 問合せ結果のバッチ処理
public static void performQuery() { NamedCache c = CacheFactory.getCache("test"); // Search for entries that start with 'c' Filter query = new LikeFilter(IdentityExtractor.INSTANCE, "c%", '\\', true); // Perform query, return keys of entries that match Set keys = c.keySet(query); // The amount of objects to process at a time final int BUFFER_SIZE = 100; // Object buffer Set buffer = new HashSet(BUFFER_SIZE); for (Iterator i = keys.iterator(); i.hasNext();) { buffer.add(i.next()); if (buffer.size() >= BUFFER_SIZE) { // Bulk load BUFFER_SIZE number of objects from cache Map entries = c.getAll(buffer); // Process each entry process(entries); // Done processing these keys, clear buffer buffer.clear(); } } // Handle the last partial chunk (if any) if (!buffer.isEmpty()) { process(c.getAll(buffer)); } } public static void process(Map map) { for (Iterator ie = map.entrySet().iterator(); ie.hasNext();) { Map.Entry e = (Map.Entry) ie.next(); out("key: "+e.getKey() + ", value: "+e.getValue()); } }
親トピック: キャッシュ内のデータの問合せ
問合せ索引の使用方法
QueryMap
内に関連付けて問合せのパフォーマンスを向上させることができます。この項には次のトピックが含まれます:
親トピック: キャッシュ内のデータの問合せ
索引の作成
QueryMap
クラスのaddIndex
メソッドを使用して、索引を作成します。問合せ可能な属性であれば、このメソッドを使用して索引を付けることができます。このメソッドには、次の3つのパラメータが含まれています。
addIndex(ValueExtractor extractor, boolean fOrdered, Comparator comparator)
例22-3は、索引の作成方法を示しています。
例22-3 索引を作成するサンプル・コード
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()
)を使用する必要があります。
適用された索引のリストはStorageManagerMBean
から取得できます。Oracle Coherenceの管理のStorageManagerMBeanを参照してください。
親トピック: 問合せ索引の使用
ユーザー定義索引の作成
アプリケーションを選択してユーザー定義索引を作成し、索引に追加されるエントリを管理できます。ユーザー定義索引は、通常、索引の保守に必要なメモリーや処理のオーバーヘッドの削減に使用されます。ユーザー定義索引を作成するには、アプリケーションで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はIndexAwareExtractor
のcreateIndex
とdestroyIndex
をコールします。IndexAwareExtractor
は、createIndex
とdestroyIndex
に渡される、エクストラクタと索引を関連付けるMap
を保持します。
親トピック: ユーザー定義索引の作成
条件付き索引の使用
条件付き索引はカスタム索引であり、上述のとおりMapIndex
インタフェースとIndexAwareExtractor
インタフェースの両方を実装し、関連付けられたフィルタを使用してエントリに索引を付ける必要があるかどうかを評価します。エントリの抽出された値は、そのフィルタがtrue
に評価されている場合、索引に追加されます。実装クラスは、それぞれConditionalIndex
とConditionalExtractor
です。
ConditionalIndex
はConditionalExtractor
によって作成されます。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);
親トピック: ユーザー定義索引の作成
バッチ問合せの実行
key set
形式の問合せをgetAll()
と組み合せると、エントリ・セット全体がクライアント上で同時にデシリアライズされないため、メモリーの消費が削減されます。また、ニア・キャッシュも利用されます。たとえば:
// key set(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 (setPageKeys.size() == PAGE_SIZE || !iter.hasNext()) { // get a block of values Map mapResult = cache.getAll(setPageKeys); // process the block // ... setPageKeys.clear(); } }
LimitFilter
を使用すると、クライアントに送信するデータ量が制限されると同時に、ページングが使用可能になります。それが正しく機能するには、LimitFilter
の使用に2つの前提条件があります。
-
問題のデータ・セットを同時に変更することはできません。
-
データはクラスタのすべての記憶域ノード間で均等に配分され、データ・セット全体の公平なサンプルが各データに含まれます。
ノート:
再配分の場合、データはすべての記憶域ノード間で均等に配分されないため、受信問合せに対して間違った結果セットになります。
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 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 ... }
親トピック: キャッシュ内のデータの問合せ
複数値属性での問合せの実行
ContainsAllFilter
、ContainsAnyFilter
およびContainsFilter
が使用されます。
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
実装を使用すると、ゼロ引数(アクセッサ)メソッドの連鎖起動が可能となります。次の例では、まずエクストラクタがリフレクションを使用して、キャッシュされているPerson
オブジェクトごとにgetName()
をコールします。その後、リフレクションを使用して、返されるString
でlength
メソッドをコールします。
ValueExtractor extractor = new ChainedExtractor("getName.length");
このエクストラクタを問合せに渡し、(たとえば)名前が10文字以内の人をすべて、問合せで選択することができます。メソッドの起動は、getName.trim.length
のように無限に連鎖できます。
POFエクストラクタおよびPOFアップデータは、SimplePofPath
クラスを使用してChainedExtractors
と同じ機能を提供します。POFエクストラクタとPOFアップデータの使用を参照してください。
親トピック: キャッシュ内のデータの問合せ
問合せ結果の一貫性チェックをスキップするオプション
Coherenceではデフォルトで、指定したフィルタに一致するエントリが問合せ結果に含まれます。ただし、ターゲットとなるパーティションが同時に変更された場合、一貫性チェックを実行すると、問合せの再評価が繰り返されることがあります。
次のオプションを使用すると、レスポンスを速くするためにCoherenceで一貫性チェックを緩和する必要があるかどうかを判断できます:
- 個々のアグリゲータのcharacteristics()をオーバーライドすることにより、アグリゲータ・レベルで実行。
たとえば:
@Override public int characteristics() { return super.characteristics() | ALLOW_INCONSISTENCIES; }
- JVM引数を介してJVMレベルで実行:
システム・プロパティ
-Dcoherence.query.retry = 0
を渡します。
集約メソッドを使用した問合せの場合、この両方のオプションを使用するとレスポンスが速くなります。フィルタ(entrySet
など)を使用した問合せの場合は、JVMオプションを使用して問合せ結果の再評価をスキップできます。
親トピック: キャッシュ内のデータの問合せ
問合せのコストと効果の評価
この項には次のトピックが含まれます:
問合せレコードの作成
com.tangosol.util.aggregator.QueryRecorder
クラスは、特定のフィルタの実行レコードまたはトレース・レコードを生成します。このクラスは、クラスタ内のすべてのノードを問い合せ、結果を集約する機能を持つ並列アグリゲータの実装です。このクラスは、問合せ内のフィルタに関する見積もりコストを示すEXPLAIN
レコードと、問合せ内の各フィルタの実際の効果を示すTRACE
レコードの2つのレコード・タイプをサポートしています。
問合せレコードを作成するには、RecordType
パラメータを指定する新しいQueryRecorder
インスタンスを作成します。aggregate
メソッドのパラメータとしてテストされるインスタンスとフィルタを含めます。次の例では、説明レコードを作成しています。
NamedCache cache = CacheFactory.getCache("mycache"); cache.addIndex(new ReflectionExtractor("getAge"), true, null); AllFilter filter = new AllFilter(new Filter[] { new OrFilter( new EqualsFilter(new ReflectionExtractor("getAge"), 16), new EqualsFilter(new ReflectionExtractor("getAge"), 19)), new EqualsFilter(new ReflectionExtractor("getLastName"), "Smith"), new EqualsFilter(new ReflectionExtractor("getFirstName"), "Bob"), }); QueryRecorder agent = new QueryRecorder(RecordType.EXPLAIN); Object resultsExplain = cache.aggregate(filter, agent); System.out.println("\n" + resultsExplain + "\n");
トレース・レコードを作成するには、RecordType
パラメータをTRACE
に変更します。
QueryRecorder agent = new QueryRecorder(RecordType.TRACE);
親トピック: 問合せのコストと効果の評価
問合せレコードの解析
問合せレコードは、問合せを構成するフィルタと索引の評価に使用されます。実行計画レコードは、フィルタの適用に関連する見積もりコストの評価に使用されます。トレース・レコードは、キー・セットの削減に対するフィルタの効果の評価に使用されます。
この項では、サンプルの実行計画レコードおよびサンプルのトレース・レコードを示し、レコードの読取りおよび解析方法について説明します。レコードは、記憶域が有効な4つのノードのクラスタにある1500エントリのサンプル問合せに基づいています。問合せは、16
歳または19
歳で、姓がSmith
、名がBob
である人を検索するフィルタで構成されています。最後に、getAge
に対する索引が追加されます。問合せレコードの実行の例を参照してください。
NamedCache cache = CacheFactory.getCache("mycache"); cache.addIndex(new ReflectionExtractor("getAge"), true, null); AllFilter filter = new AllFilter(new Filter[] { new OrFilter( new EqualsFilter(new ReflectionExtractor("getAge"), 16), new EqualsFilter(new ReflectionExtractor("getAge"), 19)), new EqualsFilter(new ReflectionExtractor("getLastName"), "Smith"), new EqualsFilter(new ReflectionExtractor("getFirstName"), "Bob"), });
この項には次のトピックが含まれます:
問合せの実行計画レコード
問合せの実行レコードは、問合せ操作の一部としてフィルタの評価の見積もりコストを提供します。コストでは、フィルタで索引を使用できるかどうかが考慮されます。コスト評価は、問合せの実行時にフィルタが適用される順序の決定に使用されます。索引を使用するフィルタは、コストが最も低く、最初に適用されます。
例22-4では、一般的な問合せ実行計画レコードを示します。レコードには、問合せ内の各フィルタを評価するための実行計画表、およびフィルタで使用できる各索引が記載されている索引ルックアップ表が含まれています。これらの列は、次のように示されます。
-
名前 – この列は、問合せ内の各フィルタの名前を示します。複合フィルタの場合は、複合フィルタ内の各フィルタの情報が示されます。
-
索引 – この列は、指定のフィルタで索引を使用できるかどうかを示します。索引が見つかった場合、表示された番号が索引ルックアップ表の索引番号に対応します。例では、
getAge()
に対して、順序付けられた単純なマップ索引(0
)が見つかりました。 -
コスト – この列は、フィルタの適用に関する見積もりコストを示します。索引が使用可能な場合、コストは1となります。索引の適用操作では、索引コンテンツに1回だけアクセスすれば済むので、値1が使用されます。例では、記憶域が有効な4つのクラスタ・メンバーがあり、したがって、コストには4つのメンバーすべての索引へのアクセスが反映されます。索引が存在しない場合、コストは
EVAL_COST * number of keys
として計算されます。EVAL_COST
値は、定数値(1000)です。これは、フィルタを使用してキー・セットを削減するために全体スキャンを実行する相対的なコストを示します。例では、評価が必要な1500のキャッシュ・エントリがあります。索引付きのエントリへの問合せは常に、索引付きでないエントリと比べると安価ですが、効果が保証されているわけではありません。
例22-4のレコードは、getAge()
の等価フィルタはコストが低いことを示しています。これは、そのフィルタが、関連付けられた索引を持ち、getLastName()
およびgetFirstName()
の前に適用されるためです。ただし、getAge()
フィルタは、安価ですが、すべてのエントリが16
か19
のいずれかであり、Bob
およびSmith
に一致するエントリがほとんどない場合、あまり効果的ではないこともあります。この場合、getLastName()
およびgetFirstName()
に索引を追加するほうがより効果的です。さらに、索引でキー・セットの削減がうまく行われていない場合、索引の作成に関連するコスト(主にメモリー消費)は無駄になります。
例22-4 サンプルの問合せ実行計画レコード
Explain Plan Name Index Cost ================================================================================== com.tangosol.util.filter.AllFilter | ---- | 0 com.tangosol.util.filter.OrFilter | ---- | 0 EqualsFilter(.getAge(), 16) | 0 | 4 EqualsFilter(.getAge(), 19) | 0 | 4 EqualsFilter(.getLastName(), Smit | 1 | 1500000 EqualsFilter(.getFirstName(), Bob | 2 | 1500000 Index Lookups Index Description Extractor Ordered ================================================================================== 0 SimpleMapIndex: Extractor=.getAge(), Ord .getAge() true 1 No index found .getLastName() false 2 No index found .getFirstName() false
親トピック: 問合せレコードの解析
問合せのトレース・レコード
問合せのトレース・レコードは、問合せ操作の一部としてフィルタの評価の実コストを提供します。コストでは、フィルタで索引を使用できるかどうかが考慮されます。問合せが実際に実行され、キー・セットの削減に関する各フィルタの効果が示されます。
例22-5では、一般的な問合せトレース・レコードを示します。レコードには、問合せ内の各フィルタの効果を示すトレース表、およびフィルタで使用できる各索引が記載されている索引ルックアップ表が含まれています。これらの列は、次のように示されます。
-
名前 – この列は、問合せ内の各フィルタの名前を示します。複合フィルタの場合は、複合フィルタ内の各フィルタの情報が示されます。
-
索引 – この列は、指定のフィルタで索引を使用できるかどうかを示します。索引が見つかった場合、表示された番号が索引ルックアップ表の索引番号に対応します。例では、
getAge()
に対して、順序付けられた単純なマップ索引(0
)が見つかりました。 -
有効性 – この列は、各フィルタの結果として実際に削減されたキー・セットの量を示します。値は、
prefilter_key_set_size
|postfilter_key_set_size
として示され、パーセンテージでも表されます。prefilter_key_set_size
値は、フィルタの評価または索引の適用前のキー・セット・サイズを表します。postfilter_key_set_size
値は、フィルタの評価または索引の適用後に残っているキー・セット・サイズを表します。複合フィルタ・エントリの場合、この値は、含まれているフィルタ全体の結果です。索引に基づいてキー・セット・サイズを削減することができなくなると、結果のキー・セットはデシリアライズされ、索引フィルタ以外のフィルタが適用されます。 -
期間 – この列は、フィルタの評価または索引の適用にかかった時間をミリ秒で示します。値0は、記録された時間がレポートのしきい値を下回ったことを示します。次の例で、
63
ミリ秒は、最初のフィルタgetLastName()
で発生したキー・セットのデシリアライズのみが必要な結果です。
例22-5のレコードは、1500エントリを、姓がSmith
で名がBob
、年齢が16
歳か19
歳の100件のエントリに絞り込むのに約63ミリ秒かかったことを示しています。1500エントリのキー・セットは、まず、getAge()
の索引を使用して300に削減されました。その結果としての300エントリ(索引を使用してこれ以上削減することはできません)は、デシリアライズされ、getLastName()
に基づいて150エントリに削減され、getFirstName()
に基づいて100に削減されました。この例では、getAge()
の索引は、実質1200エントリのキー・セットを削減できたので、消費したリソースに値する価値があります。getLastName
およびgetFirstName
の索引は、問合せ全体のパフォーマンスを高めますが、索引の作成に必要な追加のリソースに値する価値がない場合もあります。
例22-5 サンプルの問合せトレース・レコード
Trace Name Index Effectiveness Duration ================================================================================== com.tangosol.util.filter.AllFilter | ---- | 1500|300(80%) | 0 com.tangosol.util.filter.OrFilter | ---- | 1500|300(80%) | 0 EqualsFilter(.getAge(), 16) | 0 | 1500|150(90%) | 0 EqualsFilter(.getAge(), 19) | 0 | 1350|150(88%) | 0 EqualsFilter(.getLastName(), Smit | 1 | 300|300(0%) | 0 EqualsFilter(.getFirstName(), Bob | 2 | 300|300(0%) | 0 com.tangosol.util.filter.AllFilter | ---- | 300|100(66%) | 63 EqualsFilter(.getLastName(), Smit | ---- | 300|150(50%) | 63 EqualsFilter(.getFirstName(), Bob | ---- | 150|100(33%) | 0 Index Lookups Index Description Extractor Ordered ================================================================================== 0 SimpleMapIndex: Extractor=.getAge(), Ord .getAge() true 1 No index found .getLastName() false 2 No index found .getFirstName() false
親トピック: 問合せレコードの解析
問合せレコードの実行の例
次の例は、問合せレコードの作成を示す単純なクラスです。このクラスは、分散キャッシュ(mycache
)を1500 Person
オブジェクトとともにロードし、属性の索引を作成し、問合せを実行し、クラスが終了する前にコンソールに出力される問合せ実行計画レコードと問合せトレース・レコードの両方を作成します。
例22-6 問合せレコードの例
import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; import com.tangosol.util.Filter; import com.tangosol.util.aggregator.QueryRecorder; import static com.tangosol.util.aggregator.QueryRecorder.RecordType; import com.tangosol.util.extractor.ReflectionExtractor; import com.tangosol.util.filter.AllFilter; import com.tangosol.util.filter.EqualsFilter; import com.tangosol.util.filter.OrFilter; import java.io.Serializable; import java.util.Properties; public class QueryRecordExanple { public static void main(String[] args) { testExplain(); testTrace(); } public static void testExplain() { NamedCache cache = CacheFactory.getCache("mycache"); cache.addIndex(new ReflectionExtractor("getAge"), true, null); PopulateCache(cache); AllFilter filter = new AllFilter(new Filter[] { new OrFilter( new EqualsFilter(new ReflectionExtractor("getAge"), 16), new EqualsFilter(new ReflectionExtractor("getAge"), 19)), new EqualsFilter(new ReflectionExtractor("getLastName"), "Smith"), new EqualsFilter(new ReflectionExtractor("getFirstName"), "Bob"), }); QueryRecorder agent = new QueryRecorder(RecordType.EXPLAIN); Object resultsExplain = cache.aggregate(filter, agent); System.out.println("\nExplain Plan=\n" + resultsExplain + "\n"); } public static void testTrace() { NamedCache cache = CacheFactory.getCache("hello-example"); cache.addIndex(new ReflectionExtractor("getAge"), true, null); PopulateCache(cache); AllFilter filter = new AllFilter(new Filter[] { new OrFilter( new EqualsFilter(new ReflectionExtractor("getAge"), 16), new EqualsFilter(new ReflectionExtractor("getAge"), 19)), new EqualsFilter(new ReflectionExtractor("getLastName"), "Smith"), new EqualsFilter(new ReflectionExtractor("getFirstName"), "Bob"), }); QueryRecorder agent = new QueryRecorder(RecordType.TRACE); Object resultsExplain = cache.aggregate(filter, agent); System.out.println("\nTrace =\n" + resultsExplain + "\n"); } private static void PopulateCache(NamedCache cache) { for (int i = 0; i < 1500; ++i) { Person person = new Person(i % 3 == 0 ? "Joe" : "Bob", i % 2 == 0 ? "Smith" : "Jones", 15 + i % 10); cache.put("key" + i, person); } } public static class Person implements Serializable { public Person(String sFirstName, String sLastName, int nAge) { m_sFirstName = sFirstName; m_sLastName = sLastName; m_nAge = nAge; } public String getFirstName() { return m_sFirstName; } public String getLastName() { return m_sLastName; } public int getAge() { return m_nAge; } public String toString() { return "Person( " +m_sFirstName + " " + m_sLastName + " : " + m_nAge + ")"; } private String m_sFirstName; private String m_sLastName; private int m_nAge; } }
親トピック: 問合せのコストと効果の評価