ヘッダーをスキップ
Oracle® Coherenceスタート・ガイド
リリース3.7.1
B65028-01
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次

前
 
次
 

12 リードスルー、ライトスルー、ライトビハインドおよびリフレッシュアヘッドのキャッシュ

Coherenceでは、データベース、Webサービス、パッケージ化されたアプリケーション、ファイル・システムなど、任意のデータソースの透過的な読取りまたは書込みのキャッシングがサポートされますが、データベースが最も一般的に使用されます。ここでは、任意のバックエンド・データソースのことを、簡単にデータベースと呼びます。効率的なキャッシュでは、集中的な読取り専用操作および読取り/書込み操作をサポートし、読取り/書込み操作については、キャッシュとデータベースが完全に同期化されている必要があります。これに対応するため、Coherenceでは、リードスルー、ライトスルー、リフレッシュアヘッドおよびライトビハインド・キャッシングをサポートしています。


注意:

パーティション(分散)およびニア・キャッシュ・トポロジでの使用: リードスルー/ライトスルー・キャッシング(および他の類似するキャッシング形態)は、パーティション(分散)キャッシュ(ニア・キャッシュも含む)トポロジで使用することのみを目的としています。ローカル・キャッシュでは、この機能のサブセットがサポートされます。レプリケーション・キャッシュとオプティミスティック・キャッシュは使用しないでください。

12.1 プラッガブル・キャッシュ・ストア

CacheStoreは、キャッシュと基礎となるデータソースの接続に使用されるアプリケーション固有のアダプタです。CacheStore実装では、データ・アクセス・メカニズム(HibernateToplink Essentials、JPA、アプリケーション固有のJDBCコール、他のアプリケーション、メインフレーム、他のキャッシュなど)を使用してデータソースにアクセスします。CacheStoreでは、データソースからの取得データを使用したJavaオブジェクトの作成方法、データソースへのオブジェクトのマッピング方法や書込み方法、およびデータソースからのオブジェクトの削除方法が認識されます。

データソース接続方針およびデータソース-アプリケーション・オブジェクトのマッピング情報は、データソース・スキーマ、アプリケーション・クラス・レイアウト、および操作環境に固有です。そのため、このマッピング情報は、アプリケーション開発者がCacheStore実装の形式で用意する必要があります。詳細は、「CacheStore実装の作成」を参照してください。

12.2 リードスルー・キャッシング

アプリケーションから、たとえばkey Xのキャッシュ・エントリが要求されたときに、キャッシュにXがなかった場合、CoherenceはCacheStoreへの委任を自動的に実行し、基礎となるデータソースからXをロードするようにCacheStoreに要求します。Xがデータソースに存在する場合、CacheStoreはこれをロードしてCoherenceに返し、Coherenceは、将来の使用に備えてこれをキャッシュに入れます。そして最終的に、要求元のアプリケーション・コードにXが返されます。これをリードスルー・キャッシングと呼びます。リフレッシュアヘッド・キャッシュ機能を使用すると、(認識されている待機時間を削減することにより)読取りパフォーマンスがさらに向上します。詳細は、「リフレッシュアヘッド・キャッシング」を参照してください。

図12-1 リードスルー・キャッシング

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

12.3 ライトスルー・キャッシング

Coherenceでは、2つの異なる方法でデータソースの更新を処理できます。1つ目の方法は、ライトスルーです。この場合、アプリケーションでキャッシュ内のデータが更新されると(つまり、キャッシュ・エントリを変更するput(...)がコールされると)、CacheStoreを介して基礎となるデータソースにデータが正常に保存されるまで操作は完了しません(つまり、putは戻りません)。このままでは、データソースへの書込みにおいて待機時間が生じるため、書込みパフォーマンスはまったく向上しません。書き込みパフォーマンスを向上させるには、ライトビハインド・キャッシュ機能を使用します。詳細は、「ライトビハインド・キャッシュ」を参照してください。

図12-2 ライトスルー・キャッシング

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

12.4 ライトビハインド・キャッシング

ライトビハインドのシナリオでは、変更されたキャッシュ・エントリが、構成された遅延(10秒、20分、1日、1週間以上など)の経過後に、データソースに非同期で書き込まれます。ライトビハインド・キャッシュでは、データソースで更新する必要のあるデータのライトビハインド・キューが保持されます。アプリケーションでキャッシュ内のXが更新されると、Xはライトビハインド・キューに追加されます(存在しない場合。存在する場合は置き換えられます)。その後、所定のライトビハインド遅延が経過すると、CoherenceによってCacheStoreがコールされ、基礎となるデータソース内のXが最新の状態に更新されます。ライトビハインド遅延は、一連の変更が始まる時点を基準としています。つまり、キャッシュに対するデータソースのデータの遅延は、ライトビハインド遅延より遅れることはありません。

このため、一度読み取って構成された間隔で書き込む(つまり、更新頻度が少ない)シナリオになります。このアーキテクチャ・タイプには、主に4つの利点があります。

図12-3 ライトビハインド・キャッシング

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

12.5 ライトビハインドの要件

ライトビハインド・キャッシュは、1つの構成設定を調整するだけで有効化できますが、ライトビハインドを期待どおりに動作させるのはより困難です。特に、アプリケーション設計では、いくつかの設計上の問題に前もって対処しておく必要があります。

ライトビハインド・キャッシングの最も直接的は影響は、データベースの更新がキャッシュ・トランザクションの外部で実行されることです。つまり、キャッシュ・トランザクションは、(通常)データベース・トランザクションの開始前に完了します。これは、データベース・トランザクションが失敗してはならないことを意味します。これが保証できない場合、ロールバック機能を採用する必要があります。

ライトビハインドはデータベース更新の順序を並べ替えることができるため、参照整合性制約で順不同の更新をできるようにする必要があります。概念的には、データベースをISAMスタイル(更新競合がないことが保証された主キー・ベースのアクセス)の記憶域として使用します。他のアプリケーションとデータベースを共有する場合、ライトビハインド・トランザクションと外部の更新との間で競合の発生を確実に回避することはできないという新たな問題が発生します。これは、ライトビハインドの競合がヒューリスティックに処理されるか、オペレータが手動で調整する必要があることを意味します。

通常は、論理データベース・トランザクションに各キャッシュ・エントリの更新をマッピングすると、最も単純なデータベース・トランザクションが保証されるため理想的です。

ライトビハインドは、(ライトビハインド・キューがディスクに書き込まれるまで)キャッシュを効率的に記録システムで保持するため、ビジネス規則ではクラスタに永続可能な(ディスクへの永続可能ではなく)データおよびトランザクションの記憶域を許可する必要があります。

12.6 リフレッシュアヘッド・キャッシング

リフレッシュアヘッドのシナリオでは、キャッシュ・ローダーから最近アクセスされた任意のキャッシュ・エントリを、失効する前に自動的かつ非同期的にリロード(リフレッシュ)するようにキャッシュを構成できます。それにより、アクセス頻度が高いエントリがキャッシュに入った場合、そのエントリが失効によってリロードされるときに、キャッシュ・ストアが低速であるために、読取りを行うアプリケーションが影響を受けるということがなくなります。非同期リフレッシュは、有効期限が近いオブジェクトへのアクセスが発生したときにのみトリガーされます。オブジェクトへのアクセスが期限切れ後に発生した場合は、キャッシュ・ストアからの読取りが同期的に行われ、その値がリフレッシュされます。

リフレッシュアヘッド期限は、エントリの有効期間の割合として表されます。たとえば、キャッシュ・エントリの有効期間が60秒に設定されており、リフレッシュアヘッド係数が0.5に設定されているとします。オブジェクトがキャッシュされた60秒後にそのオブジェクトへのアクセスが発生すると、キャッシュ・ストアから同期読取りが実行され、その値がリフレッシュされます。ただし、30秒を超える60秒未満のエントリに対するリクエストを実行すると、キャッシュの現在の値が返され、キャッシュ・ストアから非同期のリロードがスケジュールされます。

リフレッシュアヘッドは、多数のユーザーがオブジェクトにアクセスする場合に有用です。値はキャッシュで最新の状態に維持され、キャッシュ・ストアの大量のリロードに起因する待機時間が回避されます。

キャッシュアヘッド係数の値は、coherence-cache-config.xmlファイルにある<read-write-backing-map-scheme>要素の<refresh-ahead-factor>サブ要素で指定します。リフレッシュアヘッドでは、キャッシュのエントリの有効期間(<expiry-delay>)も設定されていることを前提としています。

XMLコードの一部を示した例12-1では、ローカル・キャッシュのエントリのリフレッシュアヘッド係数を0.5、有効期限を20秒として構成しています。エントリの有効期限の10秒以内にそのエントリへのアクセスが発生した場合、そのエントリに対してキャッシュ・ストアからの非同期のリロードがスケジュールされます。

例12-1 リフレッシュアヘッド係数の指定

<distributed-scheme>
   <scheme-name>categories-cache-all-scheme</scheme-name>
   <service-name>DistributedCache</service-name>
   <backing-map-scheme>

      <read-write-backing-map-scheme>
         <scheme-name>categoriesLoaderScheme</scheme-name>
         <internal-cache-scheme>
            <local-scheme>
               <scheme-ref>categories-eviction</scheme-ref>
            </local-scheme>
         </internal-cache-scheme>

         <cachestore-scheme>
            <class-scheme>
               <class-name>
                  com.demo.cache.coherence.categories.CategoryCacheLoader
               </class-name>
            </class-scheme>
         </cachestore-scheme>
         <refresh-ahead-factor>0.5</refresh-ahead-factor>
      </read-write-backing-map-scheme>
   </backing-map-scheme>
   <autostart>true</autostart>
</distributed-scheme>
<local-scheme>
   <scheme-name>categories-eviction</scheme-name>
   <expiry-delay>20s</expiry-delay>
</local-scheme> 

12.7 キャッシュ方針の選択

この項では、いくつかのキャッシング方針の利点を比較します。

12.7.1 リードスルー/ライトスルーとキャッシュアサイドの比較

クラスタ環境のキャッシュアサイド・パターンには、一般的に2つのアプローチがあります。1つ目のアプローチでは、キャッシュ・ミスをチェックし、データベースに問い合せ、キャッシュに移入して、アプリケーション処理を継続します。この場合、様々なアプリケーション・スレッドがこの処理を同時に実行すると、多数のデータベース・アクセスが発生することになります。もう1つのアプローチとして、アプリケーションでダブルチェック・ロックが実行されることがあります(チェックはキャッシュ・エントリに対して原始的であるため、これは効果があります)。ただし、キャッシュ・ミスまたはデータベース更新(クラスタ化されたロック、追加の読取り、およびクラスタ化されたロック解除 - 最大10の追加ネットワーク・ホップ、または一般的なギガビット・イーサネット接続で6から8ミリ秒、さらに追加処理のオーバーヘッドおよびキャッシュ・エントリのロック継続期間の増加)の際に大量のオーバーヘッドが生じます。

インライン・キャッシングを使用すると、(フォルト・トレランスを確保するためにデータがバックアップ・サーバーにコピーされている間)エントリは2つのネットワーク・ホップでのみロックされます。また、ロックはパーティション所有者によりローカルで維持されます。さらに、アプリケーション・コードは完全にキャッシュ・サーバーで管理されます。つまり、ノードの管理下にあるサブセットのみが直接データベースにアクセスします(そのため、負荷とセキュリティの予測可能性が向上します)。加えて、これによりデータベース・ロジックからキャッシュ・クライアントが切り離されます。

12.7.2 リフレッシュアヘッドとリードスルーの比較

リフレッシュアヘッドでは、リードスルーと比べて待機時間が減りますが、それは将来必要とされるキャッシュ・アイテムをキャッシュが正確に予測できる場合のみです。これらの予測が完全に正確であれば、リフレッシュアヘッドにより待機時間が削減されます。余分なオーバーヘッドが発生することもありません。予測ミスの確率が高くなると、データベースに送信される不要なリクエストが増加するため、スループットへの影響が大きくなります。データベースのリクエスト処理が遅れ始めると、待機時間が長くなる可能性もあります。

12.7.3 ライトビハインドとライトスルーの比較

ライトビハインド・キャッシュの要件が満たされると、ライトスルー・キャッシュと比較して非常に高いスループットが得られ、待機時間が削減されます。また、ライトビハインド・キャッシュによりデータベース(書込みの減少)およびキャッシュ・サーバー(キャッシュ値のデシリアライズの削減)の負荷が低減されます。

12.8 冪等性

すべてのCacheStore操作は、冪等になるように(つまり、望ましくない副作用なしに繰り返し処理できるように)設計する必要があります。ライトスルーおよびライトビハインド・キャッシュの場合、フェイルオーバー処理時にキャッシュ更新のデータベース部分を再試行することにより、低コストのフォルト・トレランスを提供できます。ライトビハインド・キャッシュの場合、冪等性によって、データ整合性に影響を与えずに、複数のキャッシュ更新を単一のCacheStoreの起動にまとめることができます。

アプリケーションにライトビハインド・キャッシングの要件があっても、書込み組合せを回避する必要がある場合(たとえば、監査のため)、バージョニングされたキャッシュ・キーを(シーケンスIDを持つ通常の主キーを組み合せるなどして)作成する必要があります。

12.9 ライトスルーの制限

Coherenceでは、複数のCacheStoreインスタンス間での2フェーズのCacheStore操作はサポートされません。つまり、2つのキャッシュ・エントリが更新され、別々のキャッシュ・サーバーにあるCacheStoreモジュールへのコールがトリガーされる場合、片方のデータベース更新は成功し、もう一方は失敗する可能性があります。この場合、アプリケーション・サーバーのトランザクション・マネージャで、キャッシュアサイド・アーキテクチャ(キャッシュとデータベースを、単一トランザクション内の2つの別個のコンポーネントとして更新する)を使用することをお薦めします。多くの場合、論理コミットの失敗がないように(当然、サーバー障害が発生しないようにもする)データベース・スキーマを設計することは可能です。ライトビハインド・キャッシングでは、データベースの動作の影響をputが受けないため、この問題は回避され、根本的な問題は設計プロセスの初期段階で解決されています。

12.10 キャッシュ問合せ

キャッシュ問合せでは、キャッシュに格納されているデータのみを操作し、キャッシュにない(またはない可能性のある)データをロードするためにCacheStoreをトリガーすることはありません。そのため、CacheStoreでバックアップされたキャッシュに問い合せるアプリケーションは、その問合せに必要なすべてのデータがあらかじめロードされていることを確認する必要があります。効率性を考慮して、アプリケーションの起動時にデータ・セットをデータベースから直接キャッシュに送信して、ほとんどのバルク・ロード操作を実行しておく必要があります(NamedCache.putAll()を使用して、データのブロックをキャッシュにまとめる)。ローダー・プロセスでは、制御可能なCachestoreパターンを使用して、データベースへの循環更新を無効にする必要があります。CacheStoreは、起動サービス(クラスタ全体にエージェントを送信して、各JVMのローカル・フラグを変更する)を使用するか、レプリケーション・キャッシュ(別のキャッシュ・サービス)に値を設定して、CacheStoreメソッドの起動ごとにそれを読み取ることによって管理できます(一般的なデータベース操作と比較すると、オーバーヘッドが最低限に保たれる)。Coherenceのクラスタ化されたJMX機能により、カスタムMBeanも簡単に実装できます。

12.11 CacheStoreの実装の作成

CacheStore実装はプラッガブルで、データソースのキャッシュの使用状況に応じて、次の2つのインタフェースのいずれかを実装する必要があります。

これらのインタフェースは、com.tangosol.net.cacheパッケージにあります。CacheLoaderインタフェースには、load(Object key)およびloadAll(Collection keys)の2つのメイン・メソッドがあります。またCacheStoreインタフェースには、store(Object key, Object value)storeAll(Map mapEntries)erase(Object key)およびeraseAll(Collection colKeys)メソッドが追加されました。

実装例は、『Oracle Coherence開発者ガイド』を参照してください。

12.12 CacheStore実装のグラグイン

CacheStoreモジュールをプラグインするには、distributed-schemebacking-map-schemecachestore-schemeまたはread-write-backing-map-schemeキャッシュ構成要素内でCacheStore実装クラス名を指定します。

read-write-backing-map-schemeでは、com.tangosol.net.cache.ReadWriteBackingMapが構成されます。このバッキング・マップは、データを実際にキャッシュする内部マップ(internal-cache-schemeを参照)と、データベースと相互作用するCacheStoreモジュール(cachestore-schemeを参照)の2つの主要素で構成されます。

例12-2は、CacheStoreモジュールを指定するキャッシュ構成を示しています。<init-params>要素には、CacheStoreコンストラクタに渡されるパラメータの順序付けられたリストが格納されます。{cache-name}構成マクロを使用してキャッシュ名をCacheStore実装に渡すことにより、これがデータベース表にマップされます。使用可能なすべてのマクロの一覧は、『Oracle Coherence開発者ガイド』を参照してください。

ライトビハインドおよびリフレッシュアヘッドの構成方法の詳細は、read-write-backing-map-schemeを参照してください。その際には、write-batch-factorrefresh-ahead-factorwrite-requeue-thresholdおよびrollback-cachestore-failures要素に注意してください。

例12-2 CacheStoreモジュールの例

<?xml version="1.0"?>

<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
   xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config
   coherence-cache-config.xsd">
   <caching-scheme-mapping>
      <cache-mapping>
         <cache-name>com.company.dto.*</cache-name>
         <scheme-name>distributed-rwbm</scheme-name>
      </cache-mapping>
   </caching-scheme-mapping>

   <caching-schemes>
      <distributed-scheme>
         <scheme-name>distributed-rwbm</scheme-name>
         <backing-map-scheme>
            <read-write-backing-map-scheme>

            <internal-cache-scheme>
               <local-scheme/>
            </internal-cache-scheme>

            <cachestore-scheme>
               <class-scheme>
                  <class-name>com.company.MyCacheStore</class-name>
                     <init-params>
                        <init-param>
                           <param-type>java.lang.String</param-type>
                           <param-value>{cache-name}</param-value>
                        </init-param>
                     </init-params>
                  </class-scheme>
               </cachestore-scheme>
            </read-write-backing-map-scheme>
         </backing-map-scheme>
      </distributed-scheme>
   </caching-schemes>
</cache-config>

注意:

スレッド・カウント: CacheStoreモジュールを使用すると、キャッシュ・サービス・スレッドの消費が著しく増加します(最速のデータベースを選択しても、インメモリー構造の更新より桁違いに処理が遅くなります)。そのため、キャッシュ・サービスのスレッド数を増やす必要があります(通常10 - 100の範囲)。スレッド・プールが不十分な場合の最も顕著な症状は、(背後にあるデータベースの処理が増加せずに)キャッシュ・リクエストの待機時間が増加することです。

12.13 実装に関する考慮事項

CacheStoreを実装する場合は、次のことに注意してください。

12.13.1 リエントラント・コール

CacheStore実装では、ホスティングしているキャッシュ・サービスをコールバックしないでください。これには、内部的にCoherenceキャッシュ・サービスを参照する可能性があるOR/Mソリューションも含まれます。別のキャッシュ・サービス・インスタンスへのコールも可能ですが、深くネストされたコールは避けるように注意する必要があります。コールのそれぞれがキャッシュ・サービス・スレッドを消費するため、キャッシュ・サービス・スレッド・プールを使い果たすと、デッドロックが発生する可能性があります。

12.13.2 キャッシュ・サーバー・クラスパス

キャッシュ・エントリのクラス(値オブジェクト、データ転送オブジェクトとも呼ばれる)は、キャッシュ・サーバー・クラスパスに置く必要があります(キャッシュ・サーバーは、キャッシュ・エントリをシリアライズ/デシリアライズしてCacheStoreモジュールと相互作用する必要があるため)。

12.13.3 CacheStoreのコレクション操作

キャッシュがライトビハインドとして構成され、<write-batch-factor>が構成されている場合に使用される可能性が最も高いのは、CacheStore.storeAllメソッドです。Coherenceでは、CacheLoader.loadAllメソッドも使用されます。同様の理由で、それを最初に使用する際にはリフレッシュアヘッドを有効にする必要があると考えられます。

12.13.4 接続プール

データベース接続は、コンテナ接続プール(またはサード・パーティの接続プール)から取得するか、スレッドローカルの遅延初期化パターンを使用して取得する必要があります。専用キャッシュ・サーバーは、多くの場合、管理コンテナなしでデプロイされるため、後者が最も魅力的な選択になります。ただし、過度な同時データベース接続を回避するように、キャッシュ・サービスのスレッド・プール・サイズは制限する必要があります。