SOA に関するヒント
分散キャッシュを使ってスケーラビリティのボトルネックに対処する
Iqbal Khan
Web アプリケーションが急増し、ようやく高いトラフィック使用率に適応できるようになったら、また次の大きな波として、サービス指向アーキテクチャ (SOA) が登場しました。SOA は、スケーラビリティがきわめて高いアプリケーションを開発するための標準の方法となるよう意図されています。Windows Azure のようなクラウド コンピューティング プラットフォームは、SOA がこの目標を実現するにあたって大きな一歩となります。
SOA によって、ユーザーは、アプリケーションをインターネット経由で複数の場所、組織内の複数の部署、および複数の企業間に分散できます。そのうえ、組織内の既存のコードを再利用できるだけでなく、さらに重要なことに、さまざまなビジネス部門にまたがった共同作業が可能になります。
SOA アプリケーションは、通常、負荷分散を目的とした環境内のサーバー ファームに展開されます。これは、アプリケーションにかかる大きな負荷にできるだけ多く対処できるようにするためです。したがって、ここで問題となるのは、SOA アプリケーションのパフォーマンスとスケーラビリティの両方を高めるためには何を考慮すべきかということです。
SOA はその設計からスケーラビリティを実現するよう意図されていますが、本当の意味でスケーラビリティを実現する前に対処しなければならない多くの問題があります。このような問題には、SOA アプリケーションのコーディング方法もかかわってきますが、最も重要なボトルネックは、多くの場合、データの格納方法とデータへのアクセス方法に関連します。今回の記事では、こうした問題について説明し、いくつか解決策を紹介します。
スケーラビリティのボトルネックの特定
真の SOA アプリケーションは、アプリケーション アーキテクチャの面では容易に拡張できます。SOA アプリケーションは、サービス コンポーネントとクライアント アプリケーションの 2 つのコンポーネントから成り立っています。Web アプリケーション、他のサービス、または他の任意のアプリケーションがクライアント アプリケーションになることができますが、いずれの場合も処理の実行を SOA サービス コンポーネントに依存します。
SOA を支える重要な考え方の 1 つは、アプリケーションを小さなコンポーネントに分割し、これらのコンポーネントを複数のサーバーで個別のサービスとして実行できるようにすることです。
このようなサービスは、可能な限りステートレスにするのが理想的です。ステートレスとは、サービスが複数回呼び出されるときに、各呼び出しにまたがってデータを保持しないことを意味します。ステートレスであれば、複数のコンピューターで複数のサービスを実行できます。データが最後にあった場所に依存しないため、複数のサービス呼び出しにまたがって、特定のサーバーにデータが保持されることはありません。
したがって、SOA アプリケーションのアーキテクチャは本質的にスケーラブルといえます。つまり、複数のサーバーや複数のデータセンターに容易に拡張できます。しかし、SOA アプリケーションといえども、他のすべてのアプリケーションと同様、データを操作する必要があり、これが問題になることがあります。こうしたデータ アクセスが、スケーラビリティのボトルネックになります。ボトルネックには、なんらかのデータベース (通常はリレーショナル データベース) に格納されるアプリケーション データがかかわってくるのが一般的です。SOA アプリケーションでセッション データを使用する場合は、セッション データの記憶域もスケーラビリティのボトルネックになる可能性があります。
ある SOA アプリケーションが他の SOA アプリケーションに依存する場合は、これもパフォーマンスとスケーラビリティが低下する側面の 1 つになることがあります。たとえば、アプリケーションからあるサービスを呼び出して処理を実行するとします。しかし、呼び出されたサービスも別のサービスを呼び出すとします。これらのサービスは、同じイントラネット上にある場合もあれば、他の場所にあって WAN 経由で通信する場合もあります。こうしたデータのやり取りには負荷がかかります。このような呼び出しが何回も繰り返される場合は、アプリケーションを効果的に拡張することはできません。つまり、スケーラビリティのボトルネックが発生する側面となります (図 1 参照)。
図 1 スケーラビリティのボトルネックが発生する可能性がある SOA アーキテクチャ
パフォーマンスを向上するコード
SOA アプリケーションのパフォーマンス向上に役立つ、プログラミング技法がいくつかあります。
1 つは、1 回の呼び出しで多くの処理を実行する "チャンキーな" Web メソッド呼び出しを使用するように、アプリケーションを設計する方法です。つまり、SOA クライアント アプリケーションと SOA サービス層の間で頻繁に呼び出しを行わないようにします。クライアント アプリケーションとサービス層は、同じコンピューター上、または同じデータセンター内ですら実行されないことがあり、通常、両者の距離は大きく離れています。したがって、クライアント アプリケーションからサービス層への呼び出しが少なくなるほどパフォーマンスが向上します。チャンキーな呼び出しでは、同じ処理を複数の呼び出しに分けて実行する場合よりも、1 回の呼び出しでより多くの処理が実行されます。
もう 1 つの有効な技法は、Microsoft .NET Framework でサポートされている、非同期 Web メソッド呼び出しを使用することです。これにより、SOA のクライアント アプリケーションは、サービス層の Web メソッドを呼び出して実行しながら、他の処理を続行することができます。
シリアル化の負荷も考慮すべきもう 1 つの側面となるため、不要なデータをシリアル化しないようにします。必要なデータだけを送受信することで、実行するシリアル化の種類を幅広く選択できます。
適切な通信プロトコルの選択
SOA アプリケーションを Windows Communication Foundation (WCF) で開発する場合、SOA クライアントが SOA サービスと通信するプロトコルには、HTTP、TCP、および名前付きパイプの 3 種類があります。
クライアントとサービスがどちらも WCF で開発され、同じコンピューターで実行されていれば、名前付きパイプによって最高のパフォーマンスが実現されます。名前付きパイプは、クライアント プロセスとサーバー プロセス間で共有メモリを使用します。
SOA クライアントとサービスーがどちらも WCF で開発され、同じイントラネット上の異なるコンピューターで実行されている場合は TCP が適しています。TCP は HTTP よりも高速ですが、TCP 接続は複数の呼び出しが行われている間開いたままになるため、各 WCF 呼び出しを別のサーバーに自動的にルーティングすることはできません。接続プールを使用する NetTcpBinding オプションを使用すれば、TCP 接続を頻繁に期限切れにしてから再開できるため、接続を別のサーバーにルーティングして、一種の負荷分散を実現できます。
ただし、TCP ではソケット接続が頻繁に切断される傾向があることから、WAN 経由での機能の信頼性が高くないことに注意してください。SOA クライアントと SOA サービスが WCF に基づいていない場合、または異なる場所でホストされている場合は、HTTP を使用するのが最適です。HTTP は TCP ほど高速ではありませんが、負荷分散によってスケーラビリティが大幅に向上します。
クライアントのパフォーマンス向上にキャッシュを使用
キャッシュを慎重に使用すると、SOA クライアントのパフォーマンスを実際に向上できます。SOA クライアントからサーバー層に Web メソッド呼び出しを行うときに、クライアント アプリケーション側で結果をキャッシュします。その後、次回この SOA クライアントが同じ Web メソッド呼び出しを行う必要がある場合に、キャッシュからその結果を取得します。
クライアント側でデータをキャッシュすることで、SOA のクライアント アプリケーションからサービス層への呼び出しの回数が減少します。負荷の高い SOA サービスの呼び出しを行う必要がないため、パフォーマンスが向上します。また、サービス層の全体負荷が軽減され、スケーラビリティが向上します。図 2 に、キャッシュを使用する WCF クライアントを示します。
図 2 WCF クライアントでのキャッシュ
using System;
using Client.EmployeeServiceReference;
using Alachisoft.NCache.Web.Caching;
namespace Client {
class Program {
static string _sCacheName = "mySOAClientCache";
static Cache _sCache = NCache.InitializeCache(_sCacheName);
static void Main(string[] args) {
EmployeeServiceClient client =
new EmployeeServiceClient("WSHttpBinding_IEmployeeService");
string employeeId = "1000";
string key = "Employee:EmployeeId:" + employeeId;
// first check the cache for this employee
Employee emp = _sCache.Get(key);
// if cache doesn't have it then make WCF call
if (emp == null) {
emp = client.Load("1000");
// Now add it to the cache for next time
_sCache.Insert(key, emp);
}
}
}
}
多くの場合、クライアントはサービス層から物理的に分離され、WAN 経由で実行されます。この場合、キャッシュされているデータが最新のものかどうかを知る方法がありません。したがって、少なくとも数分から、ことによると数時間変更されないと思われるデータをアプリケーションに応じて特定し、こうしたデータ要素のみをキャッシュする必要があります。次に、キャッシュ内のこれらのデータ要素に有効期限を指定し、有効期限が切れた時点で、キャッシュからそのデータ要素が自動的に削除されるようにします。その結果、キャッシュ データが常に最新状態で適切であることを確実にできます。
サービスのスケーラビリティを向上する分散キャッシュ
キャッシュを通じて実際にスケーラビリティが高まるのは、SOA のサービス層内です。既に説明したように多くのプログラミング技法があるとしても、スケーラビリティのボトルネックは必ずしも解消されるわけではありません。なぜなら、スケーラビリティの主なボトルネックはデータ記憶域とデータ アクセスに関連しているためです。多くの場合、サービスは負荷分散されたサーバー ファームに配置されるので、サービス自体はかなり適切に拡張できます。ただし、同じ方法でデータ記憶域を拡張することはできません。したがって、データ記憶域が SOA のボトルネックとなります。
より多くのサーバーをサーバー ファームに追加することでサービス層を拡張し、追加したアプリケーション サーバーを通じてコンピューティング能力を向上できます。しかし、それらのすべての SOA のトランザクションでは、依然としてなんらかのデータが処理されます。そのデータはどこかに格納する必要がありますが、そのデータ記憶域がすぐにボトルネックになる可能性があります。
スケーラビリティの妨げとなるこのデータ記憶域の問題は、複数のレベルで改善できます。SOA サービスでは、2 種類のデータが処理されます。1 つはセッション状態データで、もう 1 つはデータベース内に収められるアプリケーション データです (図 3 参照)。この 2 種類のデータによって、スケーラビリティのボトルネックが発生します。
図 3 分散キャッシュでデータベースへの負荷を削減するしくみ
分散キャッシュへのセッション状態の格納
既定のセッション状態記憶域の制限の 1 つは、セッション状態記憶域が WCF のサービス プロセス内にあるメモリ内の記憶域であるため、Web ファームがサポートされないことです。最も適切な代替策は、WCF サービスの ASP.NET 互換モードと ASP.NET セッション状態を使用することです。これにより、StateServer、SqlServer、分散キャッシュなど、OutProc 記憶域を、セッション状態の記憶域に指定できます。
ASP.NET 互換モードを有効にする手順は、2 段階のプロセスです。まず、クラス定義で ASP.NET 互換を指定する必要があります (図 4 参照)。次に、app.config ファイルで ASP.NET 互換を指定する必要があります (図 5 参照)。図 5 では、同じ web.config ファイルで、分散キャッシュを SessionState の記憶域として指定する方法も示されています。
図 4 コードにおける WCF サービスの ASP.NET 互換の指定
using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
namespace MyWcfServiceLibrary {
[ServiceContract]
public interface IHelloWorldService {
[OperationContract]
string HelloWorld(string greeting);
}
[ServiceBehavior (InstanceContextMode =
InstanceContextMode.PerCall)]
[AspNetCompatibilityRequirements (RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
public class HelloWorldService : IHelloWorldService {
public string HelloWorld(string greeting) {
return string.Format("HelloWorld: {0}", greeting);
}
}
}
図 5 構成ファイルにおける WCF サービスの ASP.NET 互換の指定
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<sessionState cookieless="UseCookies"
mode="Custom"
customProvider="DistCacheSessionProvider"
timeout="20">
<providers>
<add name="DistCacheSessionProvider"
type="Vendor.DistCache.Web.SessionState.SessionStoreProvider"/>
</providers>
</sessionState>
<identity impersonate="true"/>
</system.web>
<system.serviceModel>
<!-- ... -->
<serviceHostingEnvironment
aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
</configuration>
セッション記憶域のオプション StateServer と SqlServer は、あまり適切には拡張されません。また、StateServer を使用すると、これが単一障害点にもなります。分散キャッシュは適切に拡張されるうえ、信頼できる方法でセッションが複数のサーバーにレプリケートされるのではるかに優れた代替策となります。
アプリケーション データのキャッシュ
アプリケーション データは、WCF サービスで群を抜いて最も多く使用されるデータになるため、その記憶域とアクセスがスケーラビリティの大きなボトルネックになります。このスケーラビリティのボトルネックに関する問題に対処するために、SOA のサービス層の実装で分散キャッシュを使用できます。分散キャッシュは、データベース内にあるデータの中から WCF サービスが数時間という短期間のうちに必要とするサブセットのみをキャッシュするために使用します。
また、分散キャッシュは、使用するアーキテクチャに基づいて拡張できるため、SOA アプリケーションのスケーラビリティは飛躍的に向上します。分散キャッシュでは、複数のサーバーにキャッシュが分散されて保持されます。ただし、SOA アプリケーションには 1 つの論理ビューが提供され、1 つのキャッシュのように見えます。実際にはキャッシュが複数のサーバー上に分散されるため、キャッシュを確実に拡張できます。サービス層とデータベースの間で分散キャッシュを使用すると、サービス層のパフォーマンスとスケーラビリティが劇的に向上します。
分散キャッシュを実装するための基本ロジックとしては、データベースからデータを取得する前に、キャッシュに既にデータが含まれているかどうかを確認します。キャッシュにデータがあれば、キャッシュから取得します。キャッシュにデータがなければ、データベースに移動してデータをフェッチし、次回のアクセスに備えてそのデータをキャッシュに格納します。図 6 に例を示します。
図 6 キャッシュを使用する WCF サービス
using System.ServiceModel;
using Vendor.DistCache.Web.Caching;
namespace MyWcfServiceLibrary {
[ServiceBehavior]
public class EmployeeService : IEmployeeService {
static string _sCacheName = "myServiceCache";
static Cache _sCache =
DistCache.InitializeCache(_sCacheName);
public Employee Load(string employeeId) {
// Create a key to lookup in the cache.
// The key for will be like "Employees:PK:1000".
string key = "Employee:EmployeeId:" + employeeId;
Employee employee = (Employee)_sCache[key];
if (employee == null) {
// item not found in the cache.
// Therefore, load from database.
LoadEmployeeFromDb(employee);
// Now, add to cache for future reference.
_sCache.Insert(key, employee, null,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.Default);
}
// Return a copy of the object since
// ASP.NET Cache is InProc.
return employee;
}
}
}
アプリケーション データをキャッシュすることにより、WCF サービスでは、多くの負荷がかかるデータベースとのやり取りを省き、近くにあるメモリ内キャッシュに含まれる使用頻度の高いトランザクション データを検索します。
キャッシュ データの有効期限の設定
有効期限には、キャッシュからデータを自動的に削除するまでに保持しておく期間を指定します。指定できる有効期限の種類には、絶対時間での有効期限と、スライド式 (アイドル時間) 有効期限の 2 種類があります。
データがキャッシュ内とデータベース内の両方に存在していると、他のユーザーやアプリケーションが、キャッシュにアクセスしないでデータベース内のデータを変更する可能性があります。このようなことが行われると、キャッシュ内のデータが古くなり、好ましくない状況になります。このデータをキャッシュ内に問題なく格納しておける期間を推測できる場合は、絶対時間で有効期限を指定します。また、"アイテムを今から 10 分後に期限切れにする" とか、"アイテムを今日の夜中に期限切れにする" という指定も可能です。キャッシュはその時点になったらこのアイテムを期限切れにします。
using Vendor.DistCache.Web.Caching;
...
// Add an item to ASP.NET Cache with absolute expiration
_sCache.Insert(key, employee, null,
DateTime.Now.AddMinutes(2),
Cache.NoSlidingExpiration,
CacheItemPriority.Default, null);
スライド式 (アイドル時間) 有効期限では、一定期間アイテムを使用する人がいない場合に期限切れにすることもできます。これは、"アイテムが 10 分間読み取られていない、または更新されていない場合に、期限切れにする" という具合に指定できます。これは、アプリケーションで一時的にデータが必要なときや、アプリケーションでデータを使い終わったときに、キャッシュ内の該当データを自動的に期限切れにする場合に便利です。ASP.NET 互換モードのセッション状態は、アイドル時間有効期限の好例です。
絶対時間の有効期限は、キャッシュに含まれるデータのコピーがデータベースのマスター コピーよりも古くなる状況を避けるのに役立ちます。これに対して、アイドル時間有効期限はまったく異なる目的で使用されます。実際には、アプリケーションでデータが不要になったら、単純にキャッシュをクリーンアップすることを目的としています。つまり、アプリケーションにこのクリーンアップを担当させるのではなく、キャッシュ自体に処理させます。
キャッシュ内のデータ リレーションシップの管理
大半のデータは、リレーショナル データベースから取得されます。リレーショナル データベースから取得されない場合でも、本質的にはリレーショナルです。たとえば、顧客オブジェクトと注文オブジェクトをキャッシュするとします。この 2 つのオブジェクトにはリレーションシップがあります。顧客は、複数の注文を行うことができます。
このようなリレーションシップが存在するときは、キャッシュ内でこれらを処理できる必要があります。つまり、キャッシュが顧客と注文の間のリレーションシップを認識する必要があります。キャッシュ内の顧客を更新または削除する場合、キャッシュ自体が該当する注文オブジェクトを自動的に削除することを望みます。これは、多くの状況でデータの整合性を保つのに役立ちます。
キャッシュがこれらのリレーションシップを把握できなければ、自分自身でこれを行う必要があります。この場合は、アプリケーションがより厄介で複雑になります。リレーションシップに関するデータを追加するときに、キャッシュにこのリレーションシップを通知しておくと、作業がはるかに簡単になります。キャッシュは、その顧客がこれまでに更新または削除されていれば、関連する注文も削除する必要があることを認識します。
ASP.NET には、CacheDependency という便利な機能が備わっています。これを使用すると、キャッシュされている異なるアイテム間のリレーションシップを追跡できます。一部の商用キャッシュにも、この機能が備わっています。ASP.NET を使用して、キャッシュされているアイテム間のリレーションシップを追跡できる方法の例を次に示します。
using Vendor.DistCache.Web.Caching;
...
public void CreateKeyDependency() {
Cache["key1"] = "Value 1";
// Make key2 dependent on key1.
String[] dependencyKey = new String[1];
dependencyKey[0] = "key1";
CacheDependency dep1 =
new CacheDependency(null, dependencyKey);
_sCache.Insert("key2", "Value 2", dep2);
}
これは、複数の層にまたがる依存関係です。つまり、A が B に依存し、B が C に依存する可能性があります。したがって、アプリケーションが C を更新したら、A も B もキャッシュから削除しなければなりません。
キャッシュとデータベースとの同期
実際には、データベースは複数のアプリケーション間で共有されていますが、それらのすべてのアプリケーションがキャッシュにアクセスするわけではないため、キャッシュをデータベースと同期する必要があります。WCF サービス アプリケーションがデータベースを更新する唯一のアプリケーションで、キャッシュも簡単に更新できるのであれば、データベースとの同期機能は不要でしょう。
しかし、実際の環境では、そのようなことはあまりありません。第三者となるアプリケーションからデータベース内のデータが更新されると、キャッシュとデータベースとの一貫性が保たれなくなります。キャッシュとデータベースとを同期することで、キャッシュがこれらのデータベースの変更を常に認識し、それに基づいてキャッシュを更新することができます。
データベースとの同期とは、通常、キャッシュ内の関連アイテムを無効にすることを意味します。そうすると、次回アプリケーションでそのアイテムが必要になると、キャッシュにそのアイテムが格納されていないため、データベースからデータを取得する必要があることになります。
ASP.NET には、SqlCacheDependency 機能が備わっています。これを使用すると、キャッシュを SQL Server 2005、SQL Server 2008、または Oracle 10g R2 以降 (基本的に、CLR をサポートしているあらゆるデータベース) と同期できます。いくつかの商用キャッシュでも、この機能が提供されます。図 7 に、SQL 依存関係を使用してリレーショナル データベースと同期する例を示します。
図 7 SQL 依存関係によるデータの同期
using Vendor.DistCache.Web.Caching;
using System.Data.SqlClient;
...
public void CreateSqlDependency(
Customers cust, SqlConnection conn) {
// Make cust dependent on a corresponding row in the
// Customers table in Northwind database
string sql = "SELECT CustomerID FROM Customers WHERE ";
sql += "CustomerID = @ID";
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.Add("@ID", System.Data.SqlDbType.VarChar);
cmd.Parameters["@ID"].Value = cust.CustomerID;
SqlCacheDependency dep = new SqlCacheDependency(cmd);
string key = "Customers:CustomerID:" + cust.CustomerID;
_ sCache.Insert(key, cust, dep);
}
ASP.NET では提供されませんが、一部の商用ソリューションで提供されている機能の 1 つに、ポーリング ベースのデータベース同期があります。DBMS で CLR がサポートされておらず、SqlCacheDependency を利用できない場合に、これが役に立ちます。その場合、キャッシュから構成可能な間隔でデータベースにポーリングして、テーブル内の特定の行の変更を検出できると便利です。それらの行が変更されると、キャッシュは対応するキャッシュされたアイテムを無効にします。
Enterprise Service Bus と SOA のスケーラビリティ
Enterprise Service Bus (ESB) とは、多くのテクノロジを使用して構築される業界概念の 1 つです。ESB は、Web サービス向けのインフラストラクチャで、コンポーネント間の通信を仲介します。簡単に言うと、ESB は複数のアプリケーションがデータを非同期に共有するためのシンプルかつ強力な方法です。ただし、組織間や WAN 経由で使用することは意図されていません。設計上、SOA アプリケーションは複数のコンポーネントに分割されることが一般的なので、アプリケーションどうしがデータを共有する必要がある場合、ESB が強力なツールとなります。
ESB を構築する方法はいくつかあります。図 8 に、分散キャッシュを使用して作成された ESB の例を示します。複数の疎結合アプリケーションや疎結合サービス コンポーネントは、ESB を使用して、ネットワーク経由でリアルタイムにデータを相互に共有できます。
図 8 分散キャッシュを使用して作成された ESB
本質的に、分散キャッシュは複数のコンピューターにまたがります。このため、分散キャッシュは非常にスケーラブルで、ESB の最初の条件を満たします。また、優れた分散キャッシュは、すべてのデータを適切にレプリケートして、キャッシュ サーバーが停止したときに、データが失われないようにします (これについては後で説明します)。最後に、優れた分散キャッシュでは、適切なイベント伝達メカニズムが提供されます。
ESB に適合するために分散キャッシュが提供しなければならないイベントは 2 種類あります。まず、ESB のすべてのクライアント アプリケーションは、ESB のデータ要素に対象データを登録でき、その対象データが変更または削除された場合に、そのことをすぐにクライアント アプリケーションに通知します。次に、キャッシュは、クライアント アプリケーションが ESB に対してカスタム イベントを発行するのを許可します。その結果、アプリケーションがネットワーク上 (もちろん、イントラネット内) のどこにあっても、ESB に接続され、このカスタム イベントの対象となる他のすべてのアプリケーションに、カスタム イベントをすぐに通知できます。
ESB のおかげで、あるアプリケーションから別のアプリケーションに SOA 呼び出しを必要とする多くのデータ交換を非常に簡単に実行できます。また、非同期のデータ共有は、シンプルな WCF サービスで簡単に実行できるようには設計されていない機能です。しかし、ESB では、非同期のデータ共有がシームレスに実行されます。クライアントが対象となるデータを事前に示していれば、データを ESB のクライアントに送り出す状況を簡単に作り出すことができます。
キャッシュのスケーラビリティと高可用性
キャッシュ トポロジとは、データが実際に分散キャッシュに格納される方法を示す用語です。さまざまな環境に適合するように設計された、多種多様なキャッシュ トポロジがあります。ここでは、パーティション キャッシュ、パーティションとレプリケーションの混合キャッシュ、およびレプリケーション キャッシュの 3 つについて説明します。
パーティション キャッシュおよびパーティションとレプリケーションの混合キャッシュは、スケーラビリティを高めるシナリオで重要な役割を果たす、2 つのキャッシュ トポロジです。この 2 つのトポロジでは、キャッシュはパーティションに分割され、各パーティションがクラスター内の異なるキャッシュ サーバーに格納されます。パーティションとレプリケーションの混合キャッシュには、さまざまなキャッシュ サーバーに格納される各パーティションのレプリカがあります。
パーティション キャッシュおよびパーティションとレプリケーションの混合キャッシュは、トランザクション データをキャッシュするための、最もスケーラブルなトポロジです (この場合、キャッシュへの書き込みは読み取りと同じくらい頻繁に行われます)。というのも、より多くのキャッシュ サーバーをクラスターに追加すると、それらのすべてのパーティションが集まって全体的なキャッシュを形成することになるため、トランザクションの容量が増加するだけでなく、キャッシュの記憶域容量も増加するためです。
3 つ目のキャッシュ トポロジであるレプリケーション キャッシュは、キャッシュ クラスター内の各キャッシュ サーバーにキャッシュ全体をコピーします。つまり、レプリケーション キャッシュでは高可用性が提供されるため、読み取りを集中的に行う場合に適しています。ただし、更新がすべてのコピーに対して同期をとって行われ、他のキャッシュ トポロジを使用したときほど高速ではないことから、データを頻繁に更新する場合は適していません。
図 9 に示すように、パーティションとレプリケーションの混合キャッシュ トポロジは、スケーラビリティと高可用性を組み合わせるのに理想的です。各パーティションにレプリカが存在するため、データが失われることはありません。
図 9 スケーラビリティ向上のためのパーティションとレプリケーションの混合キャッシュ トポロジ
高可用性は、動的なキャッシュ クラスターを使えばさらに強化できます。動的なキャッシュ クラスターでは、キャッシュやクライアント アプリケーションを停止することなく、実行時にキャッシュ クラスターのキャッシュ サーバーを追加または削除できます。分散キャッシュは運用環境で実行されるため、高可用性は重要な機能要件の 1 つです。
今後のステップ
既に説明したように、使用頻度の高いトランザクションに合わせて拡張することができない記憶域にデータが保持されていると、SOA アプリケーションを効果的に拡張することはできません。このような場合は、分散キャッシュが非常に役に立ちます。
分散キャッシュは新しい概念ですが、.NET 開発者の間で、使用頻度の高いあらゆるトランザクション アプリケーションのベスト プラクティスとして急速に受け入れられつつあります。従来のデータベース サーバーも改良されてきてはいますが、分散キャッシュなしでは、現在のアプリケーションにとって急増するスケーラビリティの要件を満たすことはできません。
今回のコラムで説明した技法は、SOA アプリケーションのスケーラビリティを新しいレベルに引き上げるのに役立つでしょう。これらの技法を今日試してみてください。分散キャッシュの詳細については、J.D. Meier、Srinath Vasireddy、Ashish Babbar、および Alex Mackman が共同執筆した MSDN ライブラリの記事 (msdn.microsoft.com/ja-jp/magazine/ff647786) を参照してください。
Iqbal Khan は、Alachisoft (英語) の代表取締役を務めている、テクノロジ エバンジェリストです。同社は、業界をリードする .NET 分散キャッシュ製品の NCache を提供しています。NCache は、エンタープライズ アプリケーションのパフォーマンスとスケーラビリティを向上する製品です。Khan は、インディアナ大学ブルーミントン校でコンピューター サイエンスの修士号を取得しています。連絡先は iqbal@alachisoft.com (英語のみ) です。
この記事のレビューに協力してくれた技術スタッフの Kirill Gavrylyuk と Stefan Schackow に心より感謝いたします。