効率的なデータキャッシングでASP.NETのパフォーマンスを改善する
著者: Mike Amundsen プリンター用 記事を転送
▼2006年1月24日 10:00 付の記事
■海外internet.com発の記事
はじめに
動的なコンテンツを使用するWebアプリケーションを扱ったことがある人なら誰でも、データアクセスが大きなボトルネックになり得ることをご存じでしょう。ASP.NETランタイムの優れた機能の1つに、System.Web.Caching名前空間を介したデータキャッシングサービスがあります。多くの開発者が直面する課題は、事前にデータキャッシングの計画をどのように立て、コードがあまり複雑にならないような形でそれをどのように効果的に実装するかということです。本稿では、ASP.NETのデータキャッシングの機能の概要を紹介します。また、データキャッシングを行う非常に簡単なプラグインアセンブリを100行に満たないコードで作成する方法についても説明します。ASP.NETページでこのアセンブリを使用すると、大量のコードを追加しなくてもWebアプリケーションのパフォーマンスを簡単に改善することができます。
ASP.NETのデータキャッシングの基本
ASP.NETのデータキャッシングでは、メモリ内にアプリケーション全体をスコープとするスレッドセーフな「バケツ」が用意され、そこに任意のシリアル化可能なオブジェクトやオブジェクトコレクションを格納することができます。この機能を利用して、負荷の高いデータベースクエリの結果や巨大なXMLドキュメント、さらには単純な配列や独自に設計したカスタムオブジェクトまで格納することができます。そうすれば、Webアプリケーションが稼働している間は、基のソースを毎回参照しなくても、このデータをキャッシュから呼び戻すことができます。しかも、データキャッシュには、キャッシュ内のアイテムの有効期間を設定できる機能があります。これを利用して、場合によっては、基になるデータが変更されたときにキャッシュを自動的に更新することができます。
そこで課題となるのは、この高性能のオブジェクトを、Webアプリケーション開発で使いやすいツールに変えることです。この記事では、この目的を実現するために、ASP.NETのデータキャッシングで最もよく使われる機能をカプセル化した1つのユーティリティクラスと、キャッシュのデータを挿入、更新、および再取得するためのショートカットを提供するヘルパークラスを作成します。
キャッシュユーティリティクラスの設計
キャッシュされたアイテムを簡単に操作できるように、主要な機能をカプセル化した単純なクラスを用意することにします。このユーティリティクラスを使うことで、キャッシュ内のアイテムの読み出し、書き込み、列挙、および削除を簡略化します。次に示すのは、ユーティリティクラスに含まれるメソッドの一覧と、各メソッドに対応する引数です。
public static string ListCache()
public static string ListCache(string mask)
public static void ClearCache()
public static void ClearCache(string mask)
public static object GetCacheItem(string key)
public static void DropCacheItem(string key)
public static void SetCacheItem(string key, object data)
public static void SetCacheItem(string key, object data,
CacheDependency dependencies)
public static void SetCacheItem(string key, object data,
int seconds)
public static void SetCacheItem(string key, object data,
int seconds, int slidingSeconds)
GetCacheItemとDropCacheItemはとても単純で、指定したアイテムを返すか削除する働きをします。ListCacheは、キャッシュ内のアイテムのリストを返します。このメソッドには2つのバージョンがあり、一方のバージョンでは、返されるアイテムを制御するためにフィルタ文字列を指定することができます。ClearCacheは、キャッシュ内のすべてのアイテム、またはフィルタ文字列に一致するアイテムを削除します。
SetCacheItemは、面白い働きをするメソッドです。このメソッドを使うと、キャッシュにアイテムを追加し、必要に応じて依存関係(通常はファイル名)や有効期間の値(オブジェクトの秒単位の最大有効期間および最大アイドル時間)を設定することができます。依存関係の設定では、キャッシュされたアイテムをディスクファイルなどの外部のアイテムに関連付けます。そのディスクファイルが変更されると、アイテムがキャッシュから自動的に削除されるというしくみです。ASP.NETでは、このような方法で「Web.config」ファイルの変更を追跡しています。
有効期間のポリシーはもう少し複雑です。オブジェクトに有効期間(このクラスでは秒単位)を設定することができます。キャッシュ内のオブジェクトが有効期間を過ぎると、そのオブジェクトは自動的に削除されます。また、オブジェクトにアイドル時間(このクラスでは秒単位)を設定することもできます。オブジェクトがアイドル状態のまま、つまりどこからも要求されずに、指定した秒数が過ぎると、そのオブジェクトはキャッシュから削除されます。次に示すのは、有効期間とアイドル時間を処理するSetCacheItemメソッドのコードです。
public static void SetCacheItem(string key, object data,
int seconds, int slidingSeconds)
{
if(slidingSeconds>0)
HttpContext.Current.Cache.Insert(key,data,null,
System.DateTime.MaxValue,TimeSpan
.FromSeconds(slidingSeconds));
else
HttpContext.Current.Cache.Insert(key,data,null,
System.DateTime.Now.AddSeconds(seconds),
TimeSpan.FromSeconds(slidingSeconds));
}
ListCacheメソッドは、キャッシュ内のすべてのアイテムの簡単なリストを(HTML形式で)返します。キャッシュ内のアイテムのリストを返す際の唯一の注意点は、キャッシュコレクションを「見て回る」ために列挙子を使う必要があることです。
public static string ListCache(string filter)
{
StringBuilder sb = new StringBuilder();
string key="";
System.Collections.IDictionaryEnumerator en =
HttpContext.Current.Cache.GetEnumerator();
while(en.MoveNext())
{
key=en.Key.ToString();
if(filter.Length!=0 && key.IndexOf(filter)!=-1)
sb.AppendFormat("{0}",key);
if(filter.Length==0)
sb.AppendFormat("{0}",key);
}
return sb.ToString();
}
ここまでは、ASP.NETのCacheクラスへのアクセスを容易にするユーティリティクラスの概要を紹介しました。次は、このクラスのメソッドを使用して、キャッシュコレクションへの高レベルのアクセスを実現するヘルパークラスを作成します。
キャッシュオブジェクトクラスのコーディング
先ほど紹介したキャッシュユーティリティは有用ですが、それだけでは、実際のWebアプリケーション内でのASP.NETのCacheクラスの扱いが大幅に簡便化されるわけではありません。そこで、よく使われるデータオブジェクトのキャッシングを実現するもう1つのクラス(ヘルパークラス)が必要になります。このヘルパークラスには、たとえば、キャッシュされたDataSetやXMLドキュメントを容易に操作できるようにするメソッドを用意します。次のコードは、キャッシュされたDataSetオブジェクトを操作するためのメソッドです。
public DataSet GetSqlDataSet(string key, string connection,
string query, bool refresh, int ttlSeconds, int idleSeconds)
{
DataSet ds = null;
try
{
if(refresh==false)
ds = (DataSet)CacheUtility.GetCacheItem(key);
}
catch {}
if(ds == null)
{
try
{
ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter(query,connection);
da.Fill(ds);
}
catch (Exception ex)
{
throw new ApplicationException("GetSqlDataSet failed.",ex);
}
if(idleSeconds>0 && ttlSeconds>0)
CacheUtility.SetCacheItem(key,ds,ttlSeconds,idleSeconds);
if(idleSeconds<1 && ttlSeconds>0)
CacheUtility.SetCacheItem(key,ds,ttlSeconds);
else
CacheUtility.SetCacheItem(key,ds);
}
return ds;
}
}
おわかりのように、このメソッドには、キャッシュコレクションからDataSetを取得するために必要な情報(key)に加え、DataSetを作成するために必要な情報(queryとconnection)、それをキャッシュに格納するために必要な情報(ttlSecondsとslidingSeconds)を渡すことができます。さらに、キャッシュの更新を「強制的に行う」ための引数(refresh)もあります。このメソッドをコードで初めて使用すると、メソッドはキャッシュにアイテムがないことを検出し、データベースクエリを実行してDataSetにデータを取り込み、そのDataSetを後から利用できるようにキャッシュに配置します。それ以降の呼び出しでは、有効期間またはアイドル時間が経過するまで、常にアイテムがキャッシュから返されます。それを過ぎると、データがデータベースから取得され、再びキャッシュに配置されます。
本稿のサンプルコードには、さまざまな目的で使用される、省略可能な引数リストを備えた他のヘルパーメソッドも含まれています。こうしたメソッドの一覧を次に示します。
public DataSet GetSqlDataSet(string key, string connection,
string query)
public DataSet GetSqlDataSet(string key, string connection,
string query, bool refresh)
public DataSet GetSqlDataSet(string key, string connection,
string query, bool refresh, int ttlSeconds)
public DataSet GetSqlDataSet(string key, string connection,
string query, bool refresh, int ttlSeconds, int idleSeconds)
public DataSet GetXmlDataSet(string key, string filename)
public DataSet GetXmlDataSet(string key, string filename,
bool refresh)
public DataSet GetXmlDataSet(string key, string filename,
bool refresh, bool depends)
public XmlDocument GetXmlDocument(string key, string url)
public XmlDocument GetXmlDocument(string key, string url,
bool refresh)
public XmlDocument GetXmlDocument(string key, string url,
bool refresh, int ttlSeconds)
public XmlDocument GetXmlDocument(string key, string url,
bool refresh, int ttlSeconds, int idleSeconds)
public XslTransform GetXslTransform(string key, string url)
public XslTransform GetXslTransform(string key, string url,
bool refresh)
public XslTransform GetXslTransform(string key, string url,
bool refresh, int ttlSeconds)
public XslTransform GetXslTransform(string key, string url,
bool refresh, int ttlSeconds, int idleSeconds)
後は、このクラスライブラリをASP.NET Webページでテストするだけです。
キャッシュライブラリのテスト
ライブラリをテストする簡単な方法は、DataSetの結果を表示するASP.NET Webページを作成することです。次のコードは、ボタンがクリックされたときにpubsデータベースのデータをDBグリッドに表示するよう要求するイベントハンドラを示しています。
void btnGetData_OnClick(object sender, EventArgs args)
{
CacheObjects co = new CacheObjects();
bool refresh = cbxRefresh.Checked;
int ttlSeconds = Int32.Parse(txtTTLSeconds.Text);
int slidingSeconds = Int32.Parse(txtSlidingSeconds.Text);
DataSet ds = co.GetSqlDataSet(key,connection,query,
refresh,ttlSeconds,slidingSeconds);
dgAuthors.DataSource = ds;
dgAuthors.DataBind();
}
ご覧のように、ごくわずかなコードを追加するだけで、このキャッシュバージョンのDataSetオブジェクトを使うことができます。図1に、結果のWebページを示します。
図1:DataSetの結果が表示されたASP.NET Webページ
本稿のダウンロードサンプルには、このキャッシュライブラリを使用して、XMLドキュメント、カスタムオブジェクト、および簡単なArrayListコレクションを処理する例も含まれています。
まとめ
本稿では、さまざまなソース(データベースやディスクファイルなど)からのデータを簡単にキャッシュしたり、キャッシュ内のデータの有効期間を簡単に制御したりするために、ASP.NETのCacheクラスを使って簡単なユーティリティを作成しました。また、データとキャッシュにすばやく簡単にアクセスするためのヘルパーメソッドを作成しました。最後に、これらのヘルパークラスをASP.NET Webページ内で使用する方法の例を紹介しました。
ここではスペースの都合で、System.Web.Caching名前空間については詳しく説明しませんでした。本稿で紹介しているヘルパークラスの例は、ごく一部の可能性を示しているにすぎません。Caching名前空間のドキュメントを丹念に調べ、自分で少し実験をしてみれば、この簡単なサンプルコードを強力なユーティリティに拡張し、ASP.NETソリューションのパフォーマンスと柔軟性を高めることができるはずです。
著者紹介
Mike Amundsen(Mike Amundsen)
国際的に有名な著者兼講演者。米国やヨーロッパを股にかけ、ソフトウェア関連の幅広いトピックについて講演や講義を行っている。プログラミングを扱った著書多数。仕事を離れているときは、ケンタッキー州の自宅で妻と3人の子供と共に過ごしている。
|