原作:Narayan Veeramani
時間:12/29 /2003
為了創建可擴展、高性能的基於WEB的應用,ASP.NET提供一個稱為數據緩存(Data Caching)的特性。數據緩存支持將頻繁訪問的數據對象可編程地存放在內存中。這一特性可擴展以廣泛地提高查詢Oracle數據庫中數據的ASP.NET應用的性能。本文講述一個策略,可用於采用Web Farm環境中的ASP.Net Web應用緩存Oracle數據庫數據。這個技巧允許在內存中緩存頻繁訪問的Oracle數據庫數據,而不是頻繁訪問數據庫來取數據。這可以幫助避免到Oracle數據庫服務器的不必要的遠路。進一步的,文章提出了一個保持緩存數據以使其始終與Oracle數據同步的實現。
ASP.Net中的數據緩存
ASP.Net中的數據緩存由Cache類和System.Web.Caching命名空間中的CacheDependency類支持。Cache類提供向緩存插入和從中取出數據的方法。CacheDependency類允許為緩存中數據項的指定其依賴項。當我們用Insert和Add方法將項目加入緩存中,可以指定一個項目的過期(expiration)策略。我們可以用Insert方法的absoluteExpiration屬性來定義緩存中一個項目的生命期。這個屬性允許你指定相應數據項過期的准確時間。也可以使用slidingExpiration屬性來指定項目過期的流逝時間(基於它被訪問的時間)。一旦一個項目過期,它從緩存中被清除。除非它再次被加入緩存中,否則再試圖訪問,將返回一個空值。
設定緩存依賴
ASP.Net使我們可以基於一個外部文件、目錄或另一個緩存項來定義一個緩存項的依賴,即所謂文件依賴與鍵依賴。若一個依賴項改變,緩存項自動失效並被從緩存中清除。當相應的數據源改變時,我們可以用這種方法來從緩存中刪除項目。例如,若我們的應用從一個XML文件中取數據並顯示在一個表格(grid)中,我們可以把文件中的數據存放到緩存中,並設定緩存依賴於那個XML文件。當XML文件被更新,數據項就從緩存中被清除出去。這一事件發生時,應用重新讀入XML文件,最新的數據項副本被再一次插入緩存中。進一步的,回調事件處理器可被設定為一個監聽者,當緩存項被刪除時得到通知。這使得我們不需要反復輪詢緩存來確定數據項是否已無效。
Oracle數據庫上的ASP.Net緩存依賴
現在考慮這樣一個情景:數據存放於Oracle數據庫中,一個ASP.NET應用通過ADO.NET來訪問。進一步,我們假設數據庫表中的數據一般是靜態的,並被這個Web應用頻繁訪問。表上的DML操作很少而對數據有很多Select。這種情況是數據緩存技術的理想應用。但不幸的是,ASP.Net並不允許設定一個緩存項依賴於存放在數據庫表中的數據。進一步,現實世界中,基於Web的系統,Web服務器和Oracle數據庫服務器總是會運行在不同的機器上,使得緩存無效操作更有挑戰性。另外,多數基於Web的應用采用Web farms,同一個應用的實例在不同的Web服務器上跑以負載均衡。這種情況使得數據庫緩存問題稍稍復雜一些。
為了進一步研究上述問題的解決方案,我們舉一個Web應用的例子來說明如何實現。例子中,我們使用VB.NET實現的ASP.NET應用,通過Oracle Data Provider for .Net (ODP)來訪問 Oracle 9i數據庫。
這個例子使用Oracle數據庫中一個名為Employee的表。我們為該表上insert, update, delete設定觸發器。這些觸發器調用一個封裝了一個Java存儲過程的PL/SQL函數。這個Java存儲過程負責更新緩存依賴的文件。
ASP.NET TIEr的VB.Net實現
我們設計了含一個回調方法的監聽類來處理緩存項無效時的通知。這個回調方法RemovedCallback用一個代理(delegate)函數來注冊。回調方法onRemove的聲明必須與CacheItemRemovedCallback代理聲明又相同的簽名。
Dim onRemove As CacheItemRemovedCallback = Nothing
onRemove = New CacheItemRemovedCallback(AddressOf RemovedCallback)
監聽事件處理方法RemovedCallback負責處理數據庫觸發器的通知,其定義如下。若緩存項失效,可用數據庫方法調用getRecordFromdatabase()從數據庫取出數據。參數”key”指從緩存中刪除的項的索引位置。參數”value”指從緩存中刪除的數據對象。參數"CacheItemRemovedReason"指從緩存中刪除數據項的原因。
PublicSub RemovedCallback(ByVal key AsString, ByVal value AsObject, 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)
EndSub
方法getRecordFromdatabase()負責查詢數據庫表Employee並返回一個DataVIEw對象引用。它使用一個名為getEmployee的存儲過程來抽象從Employee表中取數據的SQL。這個方法有一個名為p_empid的參數,表示Employee的主鍵。
PublicFunction 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
ThrowNew Exception("Exception in Database TIEr Method getRecordFromdatabase () " + ex.Message, ex)
Finally
Try
&nb
sp; cmd.Dispose()
Catch ex As Exception
Finally
cmd = Nothing
EndTry
Try
con.Close()
Catch ex As Exception
Finally
con = Nothing
EndTry
EndTry
EndFunction
函數getDatabaseConnection接受一個連接字符串(connection stirng)為參數,返回一個OracleConnection對象引用。
PublicFunction 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
ThrowNew Exception("Exception in Database TIEr Method getOracleConnection() " + ex.Message, ex)
EndTry
EndFunction
Oracle數據庫TIEr實現
定義Employee表上DML事件的觸發器體如下。這個觸發器簡單的調用一個PL/SQL包裹函數來更新名為tblemployee.txt的操作系統文件。文件副本在兩台機器(機器1和機器2)上更新。兩台機器運行同一個Web應用的不同實例來均衡負載。這裡administrator指Oracle數據庫的方案(schema)對象所有者。
begin
administrator.plfile('Machine1\\download\\ tblemployee.txt');
administrator.plfile('Machine2\\download\\ tblemployee.txt');
end;
為更新緩存依賴文件,我們需要寫一個C函數或Java存儲過程。我們的例子中選擇了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 Farm部署中的Oracle數據緩存
正如我們討論的例子中所示,Web服務器1和機器2構成了一個Web Farm來為我們的Web應用提供負載均衡。每台機器運行同一個Web應用的一個實例。在這個情況下,每個實例可以擁有自己的存放在Cache對象中的緩存數據副本。當Employee表改變,相應的數據庫觸發器更新兩台機器上的文件tblemployee.txt。每個實例都指定一個到tblemployee.txt的緩存依賴,Web Farm的兩個實例都可以正確更新,使得兩個實例上的數據緩存可以和數據庫表Employee保持同步。
結論
數據緩存是優化Oracle數據庫上ASP.NET應用的有效技巧。盡管ASP.NET不允許設定緩存的數據庫依賴,Oracle觸發器協同Java存儲過程可以擴展ASP.Net緩存的威力從而允許Oracle數據庫緩存。這個技巧也可以適用於Web Farm部署。