SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

ASP.NETにおけるOracleのデータキャッシング

Javaストアドプロシージャによるキャッシュ依存関係ファイルの更新テクニック

  • このエントリーをはてなブックマークに追加

ASP.NETには、アクセス頻度の高いデータオブジェクトをメモリ内に記憶しておく「データキャッシング」の機能が用意されています。本稿では、この機能を利用して、Oracleデータベース内のデータをクエリする際のパフォーマンスを改善する方法を紹介します。

  • このエントリーをはてなブックマークに追加

はじめに

 スケーラブルで高性能なWebベースアプリケーションを構築するために、ASP.NETには「データキャッシング」の機能が用意されています。データキャッシングとは、アクセス頻度の高いデータオブジェクトをメモリ内に記憶しておく機能です。この機能を、Oracleデータベース内のデータをクエリするASP.NETアプリケーションで利用すれば、大幅なパフォーマンスの改善が見込めます。本稿では、Webファーム環境に配備されるASP.NET Webアプリケーションを前提として、Oracleデータベースデータのキャッシング戦略を考えます。アクセス頻度の高いデータをメモリにキャッシュしておくので、データが必要となるたびにOracleデータベースまでデータを取りにいかなくても済み、当然、Oracleデータベースサーバーへの往来のかなりの部分が不必要になります。本稿では、さらに、キャッシュ内のデータとOracleデータベース内の対応データの常時同期のアイデアを提案し、そのための実装例を示します。

ASP.NETでのデータキャッシング

 ASP.NETでのデータキャッシングには、System.Web.Caching名前空間のCacheクラスとCacheDependencyクラスを使用します。Cacheクラスには、キャッシュにデータを蓄えるメソッドと、そこからデータ取り出すメソッドがあります。CacheDependencyクラスでは、キャッシュに記憶されたデータ項目に対して依存関係を指定できます。まず、ある項目をInsertメソッドまたはAddメソッドでキャッシュに追加するときに、期限ポリシーを指定できます。キャッシュ内の項目の寿命を指定するには、InsertメソッドのabsoluteExpirationパラメータを使用して、当該データ項目が期限切れとなる正確な日時を指定することができるほか、slidingExpirationパラメータを使用して、当該項目の最終アクセス日時に基づき、そこからの経過時間によって期限切れの日時を指定することもできます。期限切れとなった項目はキャッシュから取り除かれ、その後にアクセスが試みられると、null値が返されます(もちろん、それ以前に同じ項目が再びキャッシュに追加されている場合は別です)。

キャッシュに対する依存関係の指定

 ASP.NETでは、キャッシュ内の項目の依存関係を、外部ファイル、ディレクトリ、または別のキャッシュ内項目に基づいて定義できます。これをファイル依存関係やキー依存関係と呼び出します。依存関係が変化すると、キャッシュ内の項目は自動的に無効化され、キャッシュから取り除かれます。したがって、データソースに変化があったときは、この方法でキャッシュから対応項目を除去できます。たとえば、XMLファイルからデータを取り出し、それをグラフに表示するアプリケーションを書くとします。その際、ファイルから取り出したデータをキャッシュに保管し、XMLファイルに対するキャッシュ依存関係を指定しておくと、XMLファイルが更新されたときに、当該データ項目がキャッシュから取り除かれます。XMLファイルの更新というイベントが発生すると、アプリケーションは再びXMLファイルを読みにいき、データ項目の最新コピーが再度キャッシュに挿入されます。さらに、コールバックイベントハンドラを、データ項目がキャッシュから除去されるときに通知を受け取るためのリスナーとして指定することもできます。これにより、データ項目が無効化されたかどうかをキャッシュのポーリングで調べる手間が不要になります。

Oracleデータベースに対するASP.NETキャッシュ依存関係

 Oracleデータベースに格納されているデータに、ASP.NETアプリケーションからADO.NETを用いてアクセスするシナリオを考えてみましょう。このデータベーステーブル内のデータは概ね静的ですが、Webアプリケーションから頻繁に参照されるとします。つまり、テーブルに対するDML操作はきわめて少ないものの、データに対するSelect操作は非常に多いものと仮定します。これは、データキャッシングには理想的なシナリオです。しかし、残念ながら、キャッシュ項目がデータベーステーブル内のデータに依存するような依存関係は、ASP.NETでは許されていません。また、現実世界のWebベースシステムでは、WebサーバーとOracleデータベースサーバーが異なるマシン上で稼動している可能性もあり、その場合、この方式によるキャッシュの無効化はなかなか難しくなります。さらに、ほとんどのWebベースアプリケーションはWebファームに配備されており、負荷分散のため、同一アプリケーションの複数インスタンスが複数のWebサーバーで実行されています。そのため、データベースキャッシングの問題はなかなか複雑です。

 この問題の解決策を探るために、1つのWebアプリケーション例を使い、いったいどう実装できるものかを考えてみることにしましょう。使用する例は、VB.NETで実装したASP.NETアプリケーションで、これがOracle Data Provider for .NET(ODP.NET)を用いてOracle 9iデータベースと通信します。

 さて、この例では、Oracleデータベース内にある「Employee」という名前のテーブルを使用することとし、その「Employee」テーブルに対する挿入・更新・削除のためのトリガーを定義します。このトリガーはPL/SQL関数を呼び出しますが、それはJavaストアドプロシージャの単なるラッパーにすぎず、そのJavaストアドプロシージャがキャッシュ依存関係ファイルの更新を担当します。

VB.NETを用いたASP.NET層の実装

 ASP.NET層に置くリスナークラスには、キャッシュ項目の無効化通知を処理するコールバックメソッドを用意します。

 RemovedCallbackコールバックメソッドの登録には、デリゲートを使用します。onRemoveコールバックメソッドの宣言のシグネチャは、CacheItemRemovedCallbackデリゲート宣言と同じでなければなりません。

Dim onRemove As CacheItemRemovedCallback = Nothing
onRemove = New CacheItemRemovedCallback(AddressOf RemovedCallback)

 データベーストリガーからの通知を処理するRemovedCallbackリスナーイベントハンドラメソッドの定義を下に示します。キャッシュ項目が無効化されると、getRecordFromdatabase()データベースメソッドが呼び出され、データをデータベースから取り除きます。keyパラメータはキャッシュから取り除く項目のインデックス位置であり、valueパラメータはキャッシュから取り除くデータオブジェクトです。CacheItemRemovedReasonパラメータは、キャッシュからそのデータ項目を取り除く理由です。

Public Sub RemovedCallback(ByVal key As String, _
                           ByVal value As Object, _
                           ByVal reason As CacheItemRemovedReason)
   Dim Source As DataView
   Source = getRecordFromdatabase()
   Cache.Insert("employeeTable ", Source, New _
      System.Web.Caching.CacheDependency( _
         "d:\download\tblemployee.txt"), _
      Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, _
      CacheItemPriority.Normal, onRemove)
End Sub

 getRecordFromdatabase()メソッドは、「Employee」データベーステーブルをクエリし、DataViewオブジェクト参照を返します。このとき、getEmployeeというストアドプロシージャを使用してSQLを抽象化し、「Employee」テーブルからデータを取り出します。このメソッドは、「Employee」テーブルのプライマリキーを表すp_empidというパラメータを要求します。

Public Function getRecordFromdatabase (ByVal p_empid As Int32) _
                                                    As DataView
   Dim con As OracleConnection = Nothing
   Dim cmd As OracleCommand = Nothing
   Dim ds As DataSet = Nothing
   Try
      con = getDatabaseConnection( _
               "UserId=scott;Password=tiger;Data Source=testingdb;")
      cmd = New OracleCommand("Administrator.getEmployee", con)
      cmd.CommandType = CommandType.StoredProcedure
      cmd.Parameters.Add(New OracleParameter( _
                  "employeeId", OracleDbType.Int64)).Value = p_empid
      Dim param AsNew OracleParameter("RC1", OracleDbType.RefCursor)
      cmd.Parameters.Add(param).Direction = ParameterDirection.Output
      Dim myCommand AsNew OracleDataAdapter(cmd)
      ds = New DataSet
      myCommand.Fill(ds)
      Dim table As DataTable = ds.Tables(0)
      Dim index As Int32 = table.Rows.Count
      Return ds.Tables(0).DefaultView
   Catch ex As Exception
      Throw New Exception("Exception in Database Tier Method " _
         +"getRecordFromdatabase () " + ex.Message, ex)
   Finally
      Try
         cmd.Dispose()
      Catch ex As Exception
      Finally
         cmd = Nothing
      End Try
      Try
         con.Close()
      Catch ex As Exception
      Finally
         con = Nothing
      End Try
   End Try
End Function

 getDatabaseConnection関数はコネクション文字列を引数として受け入れ、OracleConnectionオブジェクト参照を返します。

Public Function getDatabaseConnection( _
               ByVal strconnection as string) As OracleConnection
   Dim con As Oracle.DataAccess.Client.OracleConnection = Nothing
   Try
      con = New Oracle.DataAccess.Client.OracleConnection
      con.ConnectionString = strconnection
      con.Open()
      Return con
   Catch ex As Exception
      Throw New Exception( _
         "Exception in Database Tier Method getOracleConnection() " _
         + ex.Message, ex)
   End Try
End Function

Oracleデータベース層の実装

 「Employee」テーブルで起こるDMLイベントのために定義されたトリガーの本体を、下に示します。このトリガーはPL/SQLラッパー関数を呼ぶだけのもので、この関数が「tblemployee.txt」というオペレーティングシステムファイルを更新します。「tblemployee.txt」ファイルは、負荷分散のために同じWebアプリケーションのインスタンスをそれぞれ別々に実行しているmachine1とmachine2という2台のマシン上にあり、その両方が更新されます。次のコードでadministratorとあるのは、Oracleデータベース内にあるスキーマオブジェクトの所有者を指しています。

begin
   administrator.plfile('machine1\\download\\ tblemployee.txt');
   administrator.plfile('machine2\\download\\ tblemployee.txt');
end;

 キャッシュ依存関係ファイルの更新には、C関数かJavaストアドプロシージャを書かなければなりません。今回は、OracleデータベースサーバーにJVMが組み込まれていて、Javaストアドプロシージャを簡単に書けるという理由から、後者を選びました。Oracleインスタンスのシステムグローバルエリア(SGA)に、Javaプールとして十分なメモリを割り振っておいてください。updateFile静的メソッドは絶対パス名をパラメータとして受け入れ、適切なディレクトリにキャッシュ依存関係ファイルを作成します。旧ファイルが存在するときは、それを削除して、新しく作り直します。

import java.io.*;
public class UpdFile {
   public static void updateFile(String filename) {
      try {
         File f = new File(filename);
         f.delete();
         f.createNewFile();
      }
      catch (IOException e)
      {
         // log exception
      }
   }
};

 pl/sqlラッパーの実装を下に示します。このラッパー関数はファイル名をパラメータとして受け取り、JavaストアドプロシージャのupdateFileメソッドを呼び出します。

(p_filename  IN  VARCHAR2)
AS LANGUAGE JAVA
NAME 'UpdFile.updateFile (java.lang.String)';

Webファーム配備でのデータベースキャッシング

 上の例では、machine1とmachine2という2台のWebサーバーがWebファームを構成し、Webアプリケーションの負荷分散を行っていました。どちらのマシンで稼動しているのも、同じWebアプリケーションのインスタンスです。このシナリオでは、どちらのインスタンスも、Cacheオブジェクトに格納されているキャッシュデータの独自コピーを持っています。「Employee」テーブルに変更があると、対応データベーストリガーが両方のマシンにある「tblemployee.txt」ファイルを更新します。どちらのWebアプリケーションインスタンスも、ローカルの「tblemployee.txt」ファイルに対してキャッシュ依存関係を定めているので、Webファームを構成する両インスタンスのキャッシュが正しく更新され、どちらのインスタンスのデータキャッシュも「Employee」データベーステーブルとの同期を維持できます。

まとめ

 Oracleデータベースを使用するASP.NETアプリケーションの最適化には、データキャッシングが効果的です。ASP.NETでは、キャッシュに対してデータベース依存関係を指定することが許されていませんが、OracleトリガーとJavaストアドプロシージャを併用することで、ASP.NETキャッシュの威力をOracleデータベースキャッシングにまで拡大できます。この手法は、Webファーム配備にも応用できます。

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Narayan Veeramani(Narayan Veeramani)

ソフトウェアのアーキテクチャ、設計、開発に10年以上携わり、現在はコロラド州ウェストミンスターのソフトウェア会社で主任ソフトウェアアーキテクトを務める。コンピュータ科学修士、コンピュータエンジニアリング学士であり、OracleとJavaの認定資格を持つ。ASP.NET、VB.NET、C#、Oracle、Java、COM、Corba、および関連する分散オブジェクト指向技術に深い造詣を持つ。メール...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/188 2005/12/14 13:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング