June 2009

Volume 24 Number 06

Velocity - 分散キャッシュを使用してより優れたデータ ドリブン アプリケーションを作成する

Aaron Dunnington | June 2009

コードは MSDN コード ギャラリーからダウンロードできます。
コードをオンラインで参照する

この記事は、Microsoft Velocity のプレリリース版に基づいています。ここに記載されているすべての情報は、変更される場合があります。

ここでは、以下について説明します。

  • Velocity の使用を開始する
  • 分散キャッシュのデータを分類する
  • 基本的な Velocity クライアント アプリケーション
  • ASP.NET のセッション状態と統合する
この記事では、次のテクノロジを使用しています。
Microsoft Velocity、ASP.NET

目次

現在のデータ処理中心アプリケーション
Velocity のメリット
データの分類
論理階層
パーティション キャッシュとローカル キャッシュ
構成ストア
Velocity の使用を開始する
クラスタの展開
基本的な Velocity クライアント アプリケーション
キャッシュを設計する
名前付きキャッシュ
キャッシュを編成する
レポートを編成する
セッションを編成する
キャッシュ層のプログラムを作成する
ASP.NET のセッション状態と統合する
今後の展開

データ ドリブン アプリケーションが普及してきました。リレーショナル ソース、サービス指向アプリケーション、シンジケーション フィード、データ処理中心のドキュメントやメッセージなど、データ ソースは無数にあります。これらのデータの宝庫を活用するために、アプリケーション アーキテクチャは進化を続けています。また、処理、ストレージ、メモリ、接続に関連する基本ハードウェアの動向やテクノロジの進歩により、アプリケーション アーキテクチャに低コストの市販ハードウェアを利用して、データ ドリブン アプリケーションを効果的にスケールアウトできるようになりました。

マイクロソフトの新しいプロジェクト、コードネーム Velocity は、分散型のメモリ内キャッシュです。Velocity では、クライアント アプリケーションが使用する分散メモリの統一されたビューを公開することにより、スケーラブルな高可用性、高パフォーマンスのアプリケーションを作成することが可能です。また、データを使用するロジックの近くに配置して基盤となるデータ ストアに対する負荷を軽減することによって、クライアント アプリケーションのパフォーマンスを高めます。Velocity のクラスタは、アプリケーションをデータ消失やデータ層の負荷増大の影響から高度に隔離して、高可用性を確保します。分散メモリを利用した Velocity では、高パフォーマンス、高可用性のアプリケーションをアプリケーション要件の増大に応じて柔軟にスケールアウトできます。

この記事では、Velocity の主要な機能を利用して、新規および既存の分散 .NET アプリケーションのスケーラビリティ、可用性、パフォーマンスをレベルアップする方法を中心に説明します。

現在のデータ処理中心アプリケーション

現在の一般的なデータ中心アプリケーションの例は、データベースを使用した単純な店舗に見られます。このアプリケーションを利用して、ユーザーは商品カタログを参照したり、ショッピング カートを利用して商品を購入したりできます。また、集計された注文データを利用して、売れ筋商品や一緒に購入されることの多い商品を表示することもできます。このアプリケーション アーキテクチャは、図 1 のように、ユーザー インターフェイス、ビジネス ロジック、データ アクセスを分離するおなじみのモデルを採用しています。ところが、このアーキテクチャには、スケーラビリティ、パフォーマンス、および可用性の面でいくつかの重要な課題があります。

fig01.gif

図 1 一般的なデータ中心の Web アプリケーション アーキテクチャ

まず、カタログ データや売れ筋データを取得するためにアプリケーションがページを読み込むたびにデータベースを操作することで、重大なスケーラビリティの問題が生じます。アプリケーションの負荷が増大するにつれて、このように頻繁なデータベース操作が行われると、リソース競合の増加やディスクからデータを取得する場合の物理的な制約が原因で、重大なスケーラビリティの制限やパフォーマンスのボトルネックが生じる可能性があります。また、このアーキテクチャでは、ページを読み込むたびにデータ層にアクセスするため、アプリケーションの待ち時間に重大な影響を与える可能性があります。

アプリケーションが Web 層で従来のキャッシュ方法を利用してデータベースの恒常的な負荷を軽減する場合に生じるもう 1 つの重要なスケーラビリティ上の課題は、状態を特定のサーバー リソースに結合することに起因します。キャッシュ状態を特定のサーバーから切り離さずに拡張する場合、ユーザー要求を個々のサーバー ノードに関連付けるためにアプリケーションは固定ルーティングなどの手法を採用する必要があります。このような方法を採用すると、ユーザー要求の特定領域で突出して大量のアクティビティが発生した場合に処理の分散が不均等になる可能性があります。

また、キャッシュを処理する Web 層のノードは可用性サブストレートで連携動作しないため、最重要アプリケーションに高可用性が保証されません。

Velocity のメリット

前述したように、Velocity は分散型のメモリ内キャッシュです。つまり、Velocity では、サーバー間でメモリを統合することによってクライアント アプリケーションに統一されたキャッシュ層を公開します。Velocity のアーキテクチャは、Velocity Windows サービスが動作するキャッシュ サーバーのリングと、Velocity クライアント ライブラリを利用して統一キャッシュ ビューとの通信を行うクライアント アプリケーションで構成されます (図 2 を参照)。

fig02.gif

図 2 Velocity のアーキテクチャ

クライアント アプリケーションは、次のように単純な Put および Get の操作で Velocity キャッシュ層にアクセスして、シリアル化可能な CLR オブジェクトを保存および取得できます。

 

CacheFactory factory = new CacheFactory(); cache = factory.GetCache("catalog"); cache.Put("Product-43", new Product("Coffee")); Product p = (Product)cache.Get("Product-43");

Velocity では、例に示した Web アプリケーション アーキテクチャで図 3 のような新しいキャッシュ層を利用できます。中間層で Velocity を使用することにより、アプリケーションのスケーラビリティ、パフォーマンス、可用性のレベルを高めることができます。これによって、データベース リソースの競合を最小限に抑え、高度なスケーラビリティを実現します。このようなスケーラビリティの向上によって、アーキテクチャでクライアントを状態ごとに個別のサーバー ノードに結合する必要がなくなるため、柔軟性が高くなります。また、データを使用するロジックの近くに配置することで、応答時間が向上し、待ち時間が短縮して、パフォーマンスも向上します。Velocity では、クラスタに冗長性を持たせて個別化することで高可用性を実現しているため、ノード障害が発生した場合にデータの消失やデータ層の負荷の急上昇が軽減されます。

fig03.gif

図 3 Velocity のキャッシュ層を使用する

ご覧のように、アプリケーションをキャッシュ層と密接に関連させることによって、さまざまな層やパターンで明示的に Velocity を利用することができます。たとえば、ビジネス ロジックで Cache-Aside パターンに直接 Velocity を使用することができます。Read-Through/Write-Behind 層をビジネス層の下に置いて、ビジネス ロジックで Velocity との通信を透過的に行うこともできます。また、Web 層で、Velocity ASP.NET セッション ストア プロバイダの統合によって Velocity を透過的に使用してセッション状態を管理できます。

データの分類

アプリケーションに設定した Velocity の機能を最大限に活用するには、一般的にキャッシュされるデータの種類を理解していることが重要です。これらのデータは、参照データ、アクティビティ データ、およびリソース データに分類されます。

参照データは、商品カタログやユーザー データなど、主として読み取り専用のデータです。参照データの書き込み頻度は低く、1 日に 1 回または 1 週間に 1 回程度ですが、参照データのスケール要件では、比較的小規模なデータに対して大量の読み取り要求が必要です。

たとえば、店舗では、ユーザーがカタログを参照することによって負荷が増大すると、膨大な商品データ ストアを取得して一覧を作成する処理が発生します。商品カタログの参照データは、変更頻度が低く、クライアント間で高度に再利用可能であるため、キャッシュに好適であり、データ層からこのデータを移動してアプリケーションの付近にキャッシュすることで、データ層の負荷が大幅に軽減されます。

売れ筋商品や一緒に購入されることの多い商品のデータも参照データに分類されます。これらのデータの報告は必ずしもリアルタイムに必要ではないため、要求されるたびに注文を分析してこれらの分類に関連する現在の商品を導き出すコストを省略できることが大きなメリットになります。

アクティビティ データは、ビジネスの活動や取引の一部であるデータです。店舗のショッピング カート操作の一部として記述されるデータはアクティビティ データです。アクティビティ データは、排他的に読み取りおよび書き込みが行われるため、通常は分割が容易です。アクティビティの有効期間の終了後 (この場合はチェックアウトの完了後)、アクティビティ データは通常はキャッシュから退避され、基盤となるストアに保存されて、今後の処理に利用されます。

現在の環境でショッピング カートの状態を維持するには、サンプル アプリケーションで固定ルーティングを使用してユーザー セッションを個々のサーバー リソースに関連付けるか、状態を基盤となるデータ ストアに永続化する必要があります。このアクティビティ データを Velocity にキャッシュすると、固定ルーティングは不要になり、主として一時的に使用されるショッピング カート データを、注文が最終的に処理されてアクティビティが完了するまで基盤データベースに保存しておく負荷も軽減されます。

リソース データは、商品在庫などのように同時に読み取りおよび書き込みが行われるデータです。注文処理では、場合によっては、在庫レベルを頻繁に監視して、在庫レベルや補充のポリシーを適用する必要があります。一方、注文を処理しながらリソース データを同時に更新し、在庫レベルの変動を反映する必要があります。リソース データを処理する際、パフォーマンスとスケーラビリティを実現するためにデータの整合性が厳密でなくなることがよくあります。たとえば、注文処理で商品が予定以上に売れた場合に、別の処理で商品を取り寄せて、在庫の不一致を調整することがあります。

Velocity をサンプル アプリケーションに統合する前に、Velocity の基本的な概念を簡単に復習しておきましょう。

論理階層

Velocity の論理階層は、コンピュータ、ホスト、名前付きキャッシュ、リージョン、キャッシュ項目で構成されます (図 4 を参照)。コンピュータは複数の Velocity サービスを実行可能であり、各サービスは 1 つのキャッシュ ホストであると見なされます。各キャッシュ ホストは、複数の名前付きキャッシュを実行できます。これらの名前付きキャッシュは複数のコンピュータ間で使用可能であり、構成で定義します。

fig04.gif

図 4 Velocity 要素の論理階層

各名前付きキャッシュには、データの論理グループ (商品カタログなど) が保存されます。名前付きキャッシュは、可用性、強制排出、有効期限に関するポリシーも設定します。各名前付きキャッシュ内には、明示的に作成されたリージョン (物理的に共存しているデータ コンテナ) が存在する場合もあります。項目は一括して取得されるため、アプリケーションでキャッシュ項目を一貫してグループ単位に処理する必要がある場合には、リージョンが便利です。また、リージョンではタグ ベースの検索やリージョン キャッシュ項目の列挙を使用できるため、使い勝手が向上します。ただし、リージョンの作成は省略可能です。Velocity では、リージョンを明示的に指定しない場合は既定のリージョンが自動的に作成されます。

暗黙的または明示的に作成されたリージョン内には、キー、オブジェクト、タグ、タイムスタンプ、バージョン、有効期限データを保持するキャッシュ項目が存在します。

パーティション キャッシュとローカル キャッシュ

現在、Velocity がサポートしているキャッシュは、パーティションとローカルの 2 種類です。パーティション キャッシュは、個々の名前付きキャッシュのホスト間にキャッシュ項目を分散します。これによって最重要な個々の名前付きキャッシュの可用性が向上し、Velocity 層のコンピュータ間で名前付きキャッシュ内に構成可能な数だけ項目のバックアップが保持されます。

ローカル キャッシュがクライアントで動作して、Velocity から取得するデータへのアクセスが高速になります。ローカル キャッシュを Velocity パーティション キャッシュと連携して使用すると、データは Velocity サーバー リングから取得され、キャッシュ項目は Velocity クライアントが動作するプロセス内にオブジェクト形式で保存されます。サーバー側の有効期限ポリシーでは、ローカル キャッシュで項目の有効期限を構成することもできます。

構成ストア

Velocity の構成ストアは、クラスタ間のキャッシュ項目の分散に関する詳細を規定するキャッシュ ポリシーとグローバル パーティション マップで構成されます。Velocity は、構成ストア内に定義されたポリシーを使用して、名前付きキャッシュで定義されたポリシーを適用し、グローバル パーティション マップを使用して、グローバル パーティション マネージャを実行します。

グローバル パーティション マネージャは Velocity のコンポーネントであり、キャッシュ項目の背景情報を伝達する役割を果たします。クライアントは最終的にこの情報を使用してクラスタとの通信を行い、データと Velocity サービス ホストとの相関関係を定義するルーティング テーブルを作成します。グローバル パーティション マネージャは Velocity サービス ホスト内で動作します。グローバル パーティション マネージャを実行中のホストが停止した場合、Velocity クラスタ内の別のホストが、構成ストアに保存されたグローバル パーティション マップからグローバル パーティション マネージャを起動して、役割を引き継ぎます。Velocity は、構成ストアをネットワーク共有に配置するか、SQL Server 内に保存できるようにします。

Velocity の使用を開始する

Velocity の使用を開始する前に、Velocity のインストール プロセス、初期クラスタの展開、基本的な Velocity クライアント アプリケーションの概要を説明します。

キャッシュ サーバー上で Velocity インストーラを使用して処理を開始すると、図 5 のようなキャッシュ ホスト構成画面が表示されます。

fig05.gif

図 5 Velocity のキャッシュ ホストの構成

キャッシュ ホスト構成画面では、Velocity ホストの実行に必要なクラスタおよびサービスの設定をキャプチャします。まず、この画面では、構成ストアをネットワーク共有に配置するか、SQL Server 内に保存するかを選択することができます。この記事のサンプルでは、[XML Based File] (XML ファイル形式) を選択して、Velocity の構成をネットワーク共有に保存します。

次に、クラスタに名前とおおよそのノード サイズが割り当てられます。クラスタ サイズの構成を使用して、クラスタ サイズに応じて内部データ構造を調整し、Velocity 内のパフォーマンスを最適化できます。クラスタの進化に応じて、後からクラスタ サイズをリセットすることも可能です。

[Service Configuration Values] (サービス構成の値) のセクションでは、現在のホストが実行されているサービス ポート、クラスタが実行されているポート、Velocity ホストが使用する最大メモリ容量をキャプチャします。

[Test Connection] (接続テスト) ボタンをクリックして、構成ストアの接続を確認します。[Save & Close] (保存して閉じる) をクリックしてインストールを完了します。ファイアウォールを実行しているコンピュータで Velocity サービスを有効にする場合は、Velocity のインストール ディレクトリにある DistributedCache.exe の例外を追加します。

追加キャッシュ ホストをインストールするには、新しい Velocity コンピュータでこのインストール手順を繰り返します。追加ホストをインストールすると、最初のクラスタ ホストのインストール時に使用されるネットワーク共有の構成ストアからクラスタ関連の設定が自動的に検出されます。この例では、3 ノードのクラスタをインストールしています。

クラスタの展開

Velocity はインストール後、Windows PowerShell で管理できます。Velocity コマンド ウィンドウを起動するには、管理者権限でインストールするとデスクトップに表示される Velocity 管理ショートカットを実行します。キャッシュ クラスタを起動するには、Start-CacheCluster コマンドを実行します。Get-CacheHost コマンドを実行すると、クラスタ全体のホストの状態が表示されます。Get-CacheStatistics を実行すると、特定の名前付きキャッシュのキャッシュ項目統計が表示されます。Windows PowerShell を使用した Velocity 管理の詳細については、Velocity のインストールに含まれているドキュメントを参照してください。

キャッシュ ホストを Velocity クラスタに参加させると、ネットワーク共有上の ClusterConfig.xml ファイルに図 6 のような XML ポリシー構成が生成されます。

図 6 Velocity の XML ポリシー構成

<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <section name="dcache" type="System.Data.Caching.DCacheSection, CacheBaseLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" /> </configSections> <dcache cluster="velsample" size="Small"> <caches> <cache type="partitioned" consistency="strong" name="default"> <policy> <eviction type="lru" /> <expiration defaultTTL="10" isExpirable="true" /> </policy> </cache> </caches> <hosts> <host clusterPort="22234" hostId="1916351620" size="2048" quorumHost="true" name="VELSAMPLE01" cacheHostName="DistributedCacheService" cachePort="22233" /> <host clusterPort="22234" hostId="1249604550" size="2048" quorumHost="false" name="VELSAMPLE02" cacheHostName="DistributedCacheService" cachePort="22233" /> <host clusterPort="22234" hostId="1016778186" size="2048" quorumHost="false" name="VELSAMPLE03" cacheHostName="DistributedCacheService" cachePort="22233" /> </hosts> <advancedProperties> <partitionStoreConnectionSettings providerName="System.Data.SqlServerCe.3.5" connectionString="\\velsample01\velocity\ConfigStore.sdf" /> </advancedProperties> </dcache> </configuration>

構成要素 dcache は、インストール時に作成された velsample クラスタを表します。この構成セクションには、ホストのインストール時にユーザーが設定したポリシー構成情報 (名前付きキャッシュ、ホスト、および構成ストアの設定) が保持されています。

caches 要素では、Velocity で既定の名前付きパーティション キャッシュを自動的に作成しています。この名前付きキャッシュ構成には、Velocity が有効期限および削除の設定を適用する際に使用するポリシー設定も保持されます。これらの設定は、この名前付きキャッシュ内のキャッシュ項目をメモリに保持する期間、およびキャッシュ項目の有効期限が切れたときの削除方法を制御します。この既定の構成の defaultTTL の値は、キャッシュ項目を 10 分間保持することを示しています。この有効期限が経過すると、"最近、最も使われていないもの (LRU)" アルゴリズムを使用してオブジェクトがメモリから強制排出されます。

これらの有効期限および強制排出の設定をオフにした方が適している場合もあります。たとえば、サンプル アプリケーションの商品カタログのように、データ セットのサイズが比較的一定していることがわかっている場合、eviction type を none に設定し、isExpirable を false に設定します。

次に、構成要素 hosts に各 Velocity ホストの名前、サイズ、アドレス情報を指定します。また、各ホストをクォーラム ホスト (リード ホスト) として使用することができます。Velocity のリード ホストは、クラスタの安定性を確保する役割を果たします。これらのリード ホストが連携して、スプリット ブレイン現象の発生を防ぎます。スプリット ブレイン現象とは、クラスタ内の通信が途絶えたときにサブクラスタが独立して動作することです。クラスタ全体が動作するためには、Velocity のリード ホストの大部分が動作している必要があるので、十分なリード ホストを用意してクラスタを分離し、これらのリード ホストの大部分に同時に障害が発生しないようにすることが重要です。

リード ホストの通信によってオーバーヘッドが増加します。そこで、必要最小限のリード ホストでクラスタを実行して、リード ホストのクォーラムを維持する方法をお勧めします。ノードが 1 ~ 3 台の小規模なクラスタの場合、小グループのリード ホストではオーバーヘッドが増加する程度が比較的低いため、すべてのノードをリード ノードとして実行しても問題ありません。それより大規模なクラスタの場合、クラスタの安定性を確保するために生じるオーバーヘッドを小さくするには、たとえば、10 ノードのクラスタに対してリード ホストを 5 台程度にして、リード ホストの割合を低くすることが推奨されます。

この記事のサンプル アプリケーションで 3 台のホストをすべてリード ホストとして実行できるようにするには、Stop-CacheCluster コマンドを実行して残りの quorumHost の値を true に更新し、Start-CacheCluster を実行します。

構成要素 advancedProperties では、パーティション マップのストレージ プロバイダと場所を指定しています。ここでは、この構成はパーティション マップ データベースのネットワークの場所を示しています。このパーティション マップ データベースは、グローバル パーティション マネージャを現在実行中のホストによって使用されます。

基本的な Velocity クライアント アプリケーション

Velocity アプリケーションを作成するための展開環境をセットアップするには、まず、新しいコンソール プロジェクトを作成します。Velocity のインストール ディレクトリに以下のアセンブリへの参照を追加します。

  • CacheBaseLibrary.dll
  • CASBase.dll
  • CASMain.dll
  • ClientLibrary.dll

次に、コンソール プロジェクトに次の構成で app.config ファイルを追加します。

<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="dcacheClient" type="System.Data.Caching.DCacheClientSection, CacheBaseLibrary" allowLocation="true" allowDefinition="Everywhere"/> </configSections> <dcacheClient deployment="routing"> <localCache isEnabled="false"/> <hosts> <host name="VELSAMPLE01" cachePort="22233" cacheHostName="DistributedCacheService"/> </hosts> </dcacheClient> </configuration>

dcacheClient セクションでは、Velocity のクライアント構成を設定します。ここでは、クライアントの展開の種類、ローカル キャッシュ オプション、およびクラスタ アドレス情報を指定しています。

deployment に設定できるのは、routing または simple のいずれかです。routing を指定すると、Velocity クライアントはクラスタ内のすべての Velocity ホストと通信できるようになります。simple の展開を指定すると、クライアントは特定の Velocity ホストと通信を行います。この場合、ターゲット ホストと通信するためのクラスタ内のネットワーク ホップが増えます。ネットワークのオーバーヘッドを小さくするには、routing での展開を指定することをお勧めします。ネットワークの制約によって、クライアントにクラスタ内の Velocity ホストのサブセット以外との通信を許可しない場合は、simple での展開が適しています。

この例では、localCache 要素で、クライアント アプリケーションのローカル キャッシュを無効にすることを指定しています。ここで isEnabled を true に設定した場合、このコンソール クライアント アプリケーションがキャッシュ項目を Velocity クラスタから取得すると、キャッシュ項目はクライアント側のアプリケーション プロセス内にオブジェクト形式で保持されます。ローカル キャッシュを使用すると、以後のクラスタへのアクセスが不要になるため、パフォーマンスがさらに向上します。

ローカル キャッシュを有効にした場合、ttlValue で、ローカル キャッシュにオブジェクトを保持する秒数を指定できます。以降、無効となったオブジェクトに対する要求が行われると、クライアントはクラスタから更新情報を取得します。

クライアントがローカル キャッシュを利用している場合、キャッシュ項目がクラスタ上で更新されていても、そのオブジェクトはローカルに取得されるということに注意してください。そのため、ローカル キャッシュは変更頻度の少ないデータに使用するのが最適です。今後のリリースで、Velocity チームは、更新されたキャッシュ項目を Velocity クラスタからクライアントに通知する通知機能を取り入れ、クライアントがその通知に従ってローカル キャッシュを更新できるようにすることを目指しています。

hosts 構成の設定では、クラスタ内の各種ホストのアドレスを指定しています。ここでは、複数のホストを指定できますが、クライアントがクラスタ内の他のホストを検出するために必要なホストは 1 つのみです。クライアント構成の各ホストは、クラスタ構成のところで示したホスト設定に相当する名前、ポート、および cacheHostName の設定を指定します。

図 7 に、オブジェクトを Velocity の統一キャッシュに配置し、キャッシュからそのオブジェクトを取得する単純なプログラムを示します。

図 7 Velocity キャッシュを使用する

using System; using System.Data.Caching; namespace VelocityClientConsole { class Program { static void Main(string[] args) { // Create the cache factory CacheFactory factory = new CacheFactory(); // Get the default named cache Cache cache = factory.GetCache("default"); // Add a string to the cache cache.Put("MyCacheKey", "Hello World"); // Get the data from the cache string s = (string)cache.Get("MyCacheKey"); Console.WriteLine(s); Console.ReadLine(); } } }

まず、using ステートメントで System.Data.Caching 名前空間内の型を参照します。Main メソッドで、CacheFactory を作成して default という名前付きキャッシュを取得します。次に、Cache クライアント インスタンスを使用して string オブジェクトを既定のキャッシュに配置し、その string オブジェクトを取得します。

CacheFactory には、クライアント構成で参照されていない Velocity クラスタとの通信を行う Cache オブジェクトを作成するための追加コンストラクタも用意されています。CacheFactory は、展開ごとの適切な具象 Cache クライアントの実装と、構成によって指定された、または CacheFactory コンストラクタでプログラムによって指定されたローカル キャッシュの設定を作成します。

Windows PowerShell でコンソール アプリケーションを実行し、Get-Cache コマンドおよび Get-CacheStatistics コマンドでキャッシュ項目が作成されていることを確認します (図 8 を参照)。Get-Cache を実行すると、項目を保持するためのリージョンが VELSAMPLE03 ホスト上に暗黙的に作成されていることがわかります。Get-CacheStatistics を実行すると、既定の名前付きキャッシュのサイズ、リージョン、およびアクセス情報が表示されます。

fig06.gif

図 8 Windows PowerShell でキャッシュ統計を表示する

キャッシュを設計する

Velocity が起動したら、次にキャッシュ ストレージを設計して、Velocity を店舗アプリケーションに統合します。この例の店舗アプリケーションでは、商品カタログ、レポート参照データ、および販売注文アクティビティのデータに Northwind データベースを使用します。このデータ モデルを図 9 に示します。

fig07.gif

図 9 店舗データ モデル

このアプリケーションでは、転送オブジェクト クラスを使用して基盤となるデータ ストアを表します。現在の店舗データ アクセス層は、転送オブジェクトの設定に System.Data.SqlClient を使用します。アプリケーションでオブジェクト関係マッピング (O/RM: object-relational mapping) テクノロジを使用してより高度な型マッピングを行う場合、これらのフレームワークによって生成されたオブジェクトを通常どおりに Velocity にキャッシュできます。データ アクセス層でシリアル化可能な CLR オブジェクトを生成すると、Velocity はこれらのオブジェクトをキャッシュできます。

名前付きキャッシュ

これらの種類のデータを制御するプロパティは参照およびアクティビティの分類によって異なるため、これらの論理単位を個別の名前付きキャッシュに区分する場合、各データの種類の要件に適したキャッシュ ポリシーを適用することでメリットを得ることができます。カタログ、レポート、およびショッピング カートのデータ用にそれぞれ個別の名前付きキャッシュを作成することによって、有効期限、強制排出、および可用性に関するポリシーを調整できます。

商品カタログは参照データに分類され、共有の読み取り専用データであるため、キャッシュに適しています。しかも、商品カタログ データは参照データの論理単位を表すので、単独の名前付きキャッシュの定義に好適です。

この新しい名前付きキャッシュを作成するには、New-Cache コマンドを使用します。

New-Cache -CacheName catalog -Eviction none -NotExpirable

カタログのキャッシュでは、データの変更頻度が少なく、カタログ サイズも予測可能であるため、強制排出の設定をオフにし、有効期限を無効にしてもかまいません。ただし、キャッシュ サイズがどの程度増加するか予測がつかない場合には、強制排出と有効期限の設定を使用して、メモリ不足状態を防ぐ必要があります。

売れ筋商品などの商品参照レポートは、カタログより頻繁に変更される可能性があるため、レポートの識別子一覧を個別の名前付きキャッシュに集計することは、基になるカタログの名前付きキャッシュ ポリシーから商品一覧を切り離せるという点で好都合です。このような商品参照関連レポートの場合、reports という別の名前付きキャッシュを作成する際に、より積極的な有効期限および強制排出方針を適用できます。

New-Cache -CacheName reports -Eviction LRU -TTL 1440

名前付きキャッシュ reports が作成され、商品識別子レポート一覧は 1 日 (1,440 分) が経過した後に有効期限切れになり、Velocity は LRU アルゴリズムを使用してオブジェクトをキャッシュから強制排出します。集計された商品識別子一覧に個別の名前付きキャッシュを使用することで、レポート関連データと商品カタログ データを別々に変更でき、異なる有効期限および強制排出ポリシーを設定することができます。このように名前付きキャッシュ reports から商品識別子一覧を使用して、売れ筋商品をカタログ キャッシュから取得できます。売れ筋レポート一覧オブジェクトは毎日期限切れになりますが、商品カタログはおそらくバックグラウンド プロセスが定期的に、またはトリガされた間隔で更新されるまでキャッシュに保持されます。

セッション関連データは、従来、読み取りと書き込みが行われるため、アクティビティ データに分類されます。ショッピング カート データは注文の完了に不可欠であるため、データの高可用性が必須です。元のアプリケーションでは、ショッピング カート データが消失するリスクを軽減するために、各注文が完了するまでこの状態を基盤となるデータ ストアに保持します。Velocity が有効化されている店舗では、ユーザーがショッピング カートの操作を進めるときに行うカート データに基づくアクティビティで、Velocity クラスタからの読み取りおよび Velocity クラスタへの書き込みを排他的に行うことができます。

新規に作成された名前付きキャッシュ session を使用して、アクティビティ関連データのプロパティでポリシーを調整できます。以前にこのデータを ASP.NET の既定のセッション状態、InProc でキャッシュしたアプリケーションは、Velocity の SessionStoreProvider の統合を使用してより柔軟なスケーリングが可能になります。この場合、状態に従ってユーザー要求を特定のサーバーに結合する固定ルーティングが不要になります。しかも、名前付きキャッシュ session の可用性を高めることにより、データがデータベースに保存されないまま消失するリスクが軽減されます。Velocity は、カート データを冗長化して保存することでクラスタを分離します。

次の New-Cache コマンドは、名前付きキャッシュ session を作成します。

New-Cache -CacheName session -Secondaries 1 -TTL 1440

–Secondaries スイッチは、この名前付きキャッシュに保存する各項目のバックアップの数を指定します。この例では、Velocity は名前付きキャッシュ session に保存される各ショッピング カートのバックアップ コピーをクラスタ全体で 1 つ別に用意します。

キャッシュを編成する

アプリケーションはカテゴリと商品、およびカテゴリ一覧と商品一覧を独立して処理できる必要があります。名前付きキャッシュ catalog 内のこれらのオブジェクトを編成するには、リージョン ベースの編成や一覧ベースの編成などの方法があります。どちらの方法も、クラスタ間のオブジェクトの分散の度合いを重視していますが、アプリケーションの要件によっては、別の編成方法の方が適している場合があります。たとえば、オブジェクト グラフをキー単位でキャッシュしたり、ページ キャッシュを実行したりできます。

Velocity のリージョンを使用して商品カタログを編成することも可能です。リージョンによる編成では、アプリケーションで、カテゴリ内の個々の商品をまとめて処理することも、個別に処理することもできるため、このサンプル シナリオには有効な方法です。たとえば、アプリケーションで、商品カテゴリ一覧全体を表示する必要がある場合と、商品詳細ページのみを表示する必要がある場合があります。商品カテゴリに関連するリージョンを使用して catalog キャッシュを編成すると、図 10 のような階層になります。

fig10.gif

図 10 リージョン ベースの編成

このキャッシュ ストレージ モデルでは、カテゴリ オブジェクトは独立して保存され、商品オブジェクトはカテゴリのメンバシップに相対したリージョンに保存されます。カタログをリージョンに基づいて編成する方法が有効な可能性があるもう 1 つの例に共存があります。リージョンのすべてのオブジェクトが同一の Velocity ホスト コンピュータに存在することが確実なため、商品カテゴリのリージョン全体が一括して取得されます。一覧ベースの編成方法では、複数のキャッシュ要求が必要です。リージョン ベースの編成では、タグに従って項目を取得することもできます。この場合、タグはキャッシュ項目のセカンダリ インデックスになります。

リージョン ベースの方法には、一括取得による最適化、商品への個別アクセス、タグ ベースの取得というメリットがありますが、項目の共存にはボトルネックも伴います。クライアント アプリケーションが特定カテゴリのリージョンを他のリージョンより積極的に使用する場合、このリージョンのホット スポットが原因で、クラスタ全体に商品を分散してデータをスケールアウトすることによって本来得られるスループットのメリットがなくなります。

商品カテゴリに一覧ベースの編成方法を使用すると、クラスタ全体に項目を分散している場合でも編成が促進されるため、データ分散がもたらすスケーラビリティのメリットを最大限に活かすことができます。リージョン ベースの編成方法では、商品グループの物理的な共存によってアクティビティのホット スポットが生じる可能性がありますが、一覧ベースの編成方法では、特定の商品カテゴリ一覧の負荷をクラスタ全体に分散できます。この編成方法では、カテゴリ オブジェクト、商品オブジェクト、および個別に保存された商品オブジェクトを識別するカテゴリ一覧オブジェクトのストレージが必要です。この一覧ベースのキャッシュの編成方法を図 11 に示します。

fig11.gif

図 11 一覧ベースの編成

一覧ベースのカタログ編成では、カタログ アクティビティの負荷がクラスタ全体に均等に分散されますが、商品カテゴリを一括して処理するには、複数のキャッシュ要求が必要です。このような場合の読み取りおよび書き込みの一括処理は、Velocity チームが今後のリリースの目標としている機能です。

レポートを編成する

名前付きキャッシュ reports でも、同様の一覧ベースの編成を利用できます。これらの参照レポートを名前付きキャッシュ catalog から切り離すことにより、レポート データを制御するポリシーとカタログ データを制御するポリシーを個別に変更できます。そのため、有効期限および強制排出のポリシーを個々のデータの種類に応じて調整できます。きわめて静的なカタログ参照データでは有効期限と強制排出の設定を無効にし、より動的なレポート データには積極的な有効期限および強制排出ポリシーを適用します。編成上、名前付きキャッシュ reports 内の商品キー一覧オブジェクトは、商品カテゴリ一覧を使用する方法と同様に catalog キャッシュ内の商品オブジェクトを識別しますが、名前付きキャッシュ全体で使用されます。

これらの集計された参照レポートに基づいて商品を取得するには、一覧ベースの方法を名前付きキャッシュ reports および catalog 全体で使用します。

セッションを編成する

読み取りおよび書き込みを排他的に行うアクティビティ ベースのシナリオでは、オブジェクト階層のストレージを使用してキャッシュの編成にアクセスする方法が最適な場合があります。たとえば、名前付きキャッシュ session では、特定のユーザーが選択した各商品に関する情報 (商品、価格、数量) を保持するショッピング カート オブジェクトを編成できます。

カタログ編成では、項目をキャッシュ全体に分散することによって高度なスケーラビリティが実現されますが、ショッピング カートのキャッシュ項目を要求間で再利用できる度合いはそれほど高くありません。各ユーザーのショッピング カートを、最上位キャッシュ項目として名前付きキャッシュ全体に分散したショッピング カート項目ではなく、1 つのキーでキャッシュすると、パフォーマンスを向上させることができます。たとえば、店舗アプリケーションが名前付きキャッシュ session に対する要求を生成すると、各ショッピング カートを 1 回の要求で一括して取得できます。名前付きキャッシュ session は可用性の高い環境で作成されているため、Velocity は各ショッピング カートのコピーをクラスタ全体で保持することによってセッション データを保護します。セッションの編成方法を図 12 に示します。

fig12.gif

図 12 セッションのキャッシュ編成

アプリケーションは Velocity API を使用してアクティビティ ベースの項目を直接維持することが可能であり、ASP.NET アプリケーションで Velocity とセッション ストア プロバイダ フレームワークとの統合を利用し、ASP.NET セッション API を使用して、Velocity を透過的にキャッシュ層として扱うことができます。

キャッシュ層のプログラムを作成する

現在のアーキテクチャ層は、ユーザー インターフェイス (Web)、ビジネス ロジック層 (BLL)、およびデータ アクセス層 (DAL) に区分されています。現在のアプリケーションは主にデータ層で生成されたデータ転送オブジェクトを処理するため、個々の種類別にデータをキャッシュすることで、アプリケーション層がわかりやすい表現で提供されるようになります。店舗の例では、キャッシュ層をビジネス層とデータ層の間に置いています。キャッシュ ロジックをビジネス層の下に組み込むことにより、ビジネス ロジックに依存する層は一様にキャッシュ ロジックの呼び出しを生成する前に行うビジネス規則の妥当性確認からメリットを得ることができ、ビジネス ロジックはキャッシュ層を透過的に使用します。また、Web 層は Velocity ASP.NET セッション ストア プロバイダの統合を利用します。

この階層化を実際に確認するため、商品カテゴリ一覧ビューを使用してキャッシュ層の動作を見てみましょう。まず、カテゴリ一覧ビューは、要求の妥当性を確認するビジネス ロジック層を呼び出した後、VelocityCatalogCache の GetProductsByCategory メソッドを呼び出してキャッシュ層を使用します (図 13 を参照)。これにより、Velocity キャッシュおよびデータ ストア間のマーシャリング データがビジネス ロジックに透過的な方法で調整されます。

図 13 GetProductsCatalog

public IList<Product> GetProductsByCategory(int id) { IList<Product> products = null; IList<int> productIDs = (IList<int>)_catalogCache.Get("ProductCategoryList-" + id); if (productIDs != null) { products = new List<Product>(productIDs.Count); foreach (int productID in productIDs) { products.Add((Product)_catalogCache.Get("Product-" + productID)); } } else { products = DataLayer.CatalogProvider.GetProductsByCategory(id); if (products != null && products.Count > 0) { productIDs = new List<int>(products.Count); foreach (Product p in products) { productIDs.Add(p.ProductID); _catalogCache.Put("Product-" + p.ProductID, p); } _catalogCache.Put("ProductCategoryList-" + id, productIDs); } } return products; }

この商品カテゴリ一覧で、キャッシュ層は Read-Through プロバイダとして機能し、ビジネス層を簡素化します。これは、Velocity を使用してキャッシュ ロジックをプログラミングするための階層化方法の 1 つにすぎません。たとえば、ビジネス レベルでの詳細な制御では、ビジネス層で Cache-Aside パターンによって直接 Velocity を使用することも有効な方法です。Velocity チームは、今後のリリースで、Read-Through/Write-Behind をサポートし、キャッシュおよび基盤ストア間のマーシャリング データを調整するためのコールバックを登録して、クライアントがラッパー層ではなく、Velocity キャッシュ API を使用してこの動作を利用できるようにすることを目標にしています。

キャッシュ層メソッド GetProductsByCategory の実装では、同時実行との関連性を意識することが重要です。このメソッドでは、現在、同時実行モデルは使用されていません。カタログは、主として変更頻度の低い読み取り専用の参照データであるため、同時実行の影響はさほど重大ではありませんが、複数のスレッドまたはクライアントで同時にキャッシュ ミスが発生した場合には、同一の更新が交互に行われる可能性があります。さまざまな種類のリソース データをキャッシュする場合のように同時実行が重要な場合には、Velocity はオプティミスティックおよびペシミスティックの両方の同時実行モデルをサポートします。

ASP.NET のセッション状態と統合する

Velocity では、ASP.NET SessionStoreProvider フレームワークを使用してキャッシュ クラスタを透過的に利用することができます。この機能により、Web アプリケーションで HttpContext.Session プロパティを使用して、指定した名前キャッシュ内の項目にアクセスできます。

この統合は、キャッシュ状態を個別のサーバーから切り離すことによってスケーラビリティ上の大きなメリットをもたらします。セッションやアクティビティに関連するデータに Velocity を使用することで、アーキテクチャでクライアントを状態ごとに個別のサーバー ノードに結合する必要がなくなるため、スケーリングの柔軟性が高くなります。

ショッピング アクティビティ データのように高可用性が要求される場合には、Velocity では、名前付きキャッシュ session の作成時の説明で示したように、冗長性を持たせることでクラスタを簡単に分離することが可能であり、データを使用するロジックの近くに配置することができます。

Velocity の SessionStoreProvider の統合を ASP.NET で利用するには、次の構成を店舗 ASP.NET アプリケーションの web.config ファイルに追加します。この構成は、店舗の ASP.NET セッション状態を、先に作成した名前付きキャッシュ session に関連付けます。

<system.web> <sessionState mode="Custom" customProvider="SessionStoreProvider"> <providers> <add name="SessionStoreProvider" type="System.Data.Caching.SessionStoreProvider, ClientLibrary" cacheName="session" /> </providers> </sessionState> </system.web>

次に、ASP.NET で名前付きキャッシュ session にアクセスするために、店舗は図 14 の Product.aspx ページのようなユーザーのショッピング カート アクティビティ データを維持します。

図 14 ASP.NET を使用してキャッシュにアクセスする

protected void AddToCartButton_Click(object sender, EventArgs e) { ShoppingCart cart = (ShoppingCart)Session["ShoppingCart"]; if (cart == null) { cart = new ShoppingCart(); Session["ShoppingCart"] = cart; } int productID = Convert.ToInt32(Request.Params["id"]); ShoppingCartItem item = cart[productID]; if (item != null) { item.Quantity++; } else { Product p = ServiceLayer.CatalogService.GetProduct(productID); cart[productID] = new ShoppingCartItem() { ProductID = productID, Quantity = 1, UnitPrice = p.UnitPrice }; } Response.Redirect("~/Cart.aspx"); }

注文が完了したら、アクティビティ ベースのショッピング カートを名前付きキャッシュ session から削除し、次のような方法で Purchase.aspx ページから基盤となるデータ ストアに反映することができます。

protected void CheckoutButton_Click(object sender, EventArgs e) { ShoppingCart cart = (ShoppingCart)Session["ShoppingCart"]; ServiceLayer.OrderService.ProcessOrder(cart, NameTextBox.Text, AddressTextBox.Text, CityTextBox.Text, RegionTextBox.Text, PostalCodeTextBox.Text, CountryTextBox.Text); Session["ShoppingCart"] = null; Response.Redirect("~/Congrats.aspx"); }

ASP.NET のセッション API で、アプリケーションは Velocity をシームレスに利用してアクティビティ ベースのデータを使用するロジックの近くに配置します。これにより、固定ルーティングなどの方法が不要になります。また、以前に名前付きキャッシュ session で有効にした高可用性のサポートによって、ショッピング カート アクティビティ データが保護されます。

今後の展開

Velocity は、分散アーキテクチャの進化と基盤ハードウェアの動向を活かして、データを使用するアプリケーションの近くに配置する環境を実現します。この記事では、Velocity の機能と原理のほんの概要を紹介したにすぎません。

サンプル店舗アプリケーションでは、Velocity を使用して、参照カタログ データ、レポート データ、およびショッピング カート アクティビティ データをそれぞれのデータの種類に適したポリシーを使用してキャッシュしています。以前には、ページを読み込むたびにカタログ データを複製する操作を行うことでスケーラビリティが制限されていましたが、基盤となるデータ ストアの負荷を軽減し、カタログ参照データをそのデータを使用するロジックの近くで認識することによってスケーラビリティとパフォーマンスを高めることができるようになりました。応答時間も向上し、基盤となるアーキテクチャは状態に基づく要求にリソースを結合する必要がなくなるため、スケーラビリティも高まります。以前のように基盤データ ストアにアクセスする必要がなくなり、注文の完了に不可欠なデータは Velocity の高可用性のサポートによって保護されます。

分散型メモリ内キャッシュの理論、Velocity の機能セット全体の概要、およびダウンロード情報の詳細については、ホワイトペーパー「マイクロソフト プロジェクト コード "Velocity"」および Velocity デベロッパー センターを参照してください。

Aaron Dunnington は、マイクロソフトの データ プログラマビリティ チームのプログラム マネージャです。連絡先は https://blogs.msdn.com/velocity (英語のみ) です。