ASP性能优化28条守则---[2] 将经常使用的数据缓存在 Application 或 Session 对象中


技巧 2: 将经常使用的数据缓存在 Application 或 Session 对象中
ASP Application 和 Session 对象为将数据缓存在内存中提供了方便的容器。您可以将数据指派到 Application 和 Session 对象中,这些数据在 HTTP 调用之间保留在内存中。Session 数据是按每个用户分别存储的,而 Application 数据则在所有用户之间共享。

什么时候将数据装载到 Application 或 Session 中呢?通常,数据是在启动 Application 或 Session 时装载。要在 Application 或 Session 启动过程中装载数据,应将适当的代码分别添加到 Application_OnStart() 或 Session_OnStart() 中。这些函数应在 Global.asa 中,如果没有,则可以添加这些函数。还可以在第一次需要时装载该数据。为此,在 ASP 页中添加一些代码(或编写一个可重复使用的脚本函数),以检查数据是否存在,如果不存在,就装载数据。这是一个传统的性能技术,称为“惰性计算” - 在您知道需要某一个值以前不计算该值。例如:

<%
Function GetEmploymentStatusList
Dim d
d = Application(?EmploymentStatusList?)
If d = ?? Then
' FetchEmploymentStatusList function (not shown)
' fetches data from DB, returns an Array
d = FetchEmploymentStatusList()
Application(?EmploymentStatusList?) = d
End If
GetEmploymentStatusList = d
End Function
%>


可以为所需要的每个数据块编写类似的函数。

应以什么格式存储数据?可以存储任何变体类型,因为所有脚本变量都是变体型。例如,您可以存储字符串、整数或数组。通常,您将以这些变量类型之一存储 ADO 记录集的内容。要从 ADO 记录集获取数据,您可以手工将数据复制到 VBScript 变量,一次一个字段。使用一个 ADO 记录集持久函数 GetRows()、GetString() 或 Save()(ADO 2.5),可加快速度且更容易一些。其详细情况已超出本文所讨论的范围,但下面给出了一个函数举例,说明使用 GetRows() 返回记录集数据的一个数组:

' Get Recordset, return as an Array
Function FetchEmploymentStatusList
Dim rs
Set rs = CreateObject(?ADODB.Recordset?)
rs.Open ?select StatusName, StatusID from EmployeeStatus?, _
?dsn=employees;uid=sa;pwd=;?
FetchEmploymentStatusList = rs.GetRows() ? Return data as an Array
rs.Close
Set rs = Nothing
End Function


对上面举例做更进一步改进,可以将 HTML 缓存为列表,而不是数组。下面是简单的示例:

' Get Recordset, return as HTML Option list
Function FetchEmploymentStatusList
Dim rs, fldName, s
Set rs = CreateObject(?ADODB.Recordset?)
rs.Open ?select StatusName, StatusID from EmployeeStatus?, _
?dsn=employees;uid=sa;pwd=;?
s = ?<select name=??EmploymentStatus??>? & vbCrLf
Set fldName = rs.Fields(?StatusName?) ' ADO Field Binding
Do Until rs.EOF
' Next line violates Don't Do String Concats,
' but it's OK because we are building a cache
s = s & ? <option>? & fldName & ?</option>? & vbCrLf
rs.MoveNext
Loop
s = s & ?</select>? & vbCrLf
rs.Close
Set rs = Nothing ' See Release Early
FetchEmploymentStatusList = s ' Return data as a String
End Function


在适当的条件下,可以将 ADO 记录集本身缓存在 Application 或 Session 作用域中。有两个警告:

必须将 ADO 标记为自由线程
必须使用断开连接的记录集。
如果不能保证满足这两个要求,则不要缓存 ADO 记录集。在下面的“非敏捷组件”和“不要缓存连接”技巧中,我们将讨论将 COM 对象存储在 Application 或 Session 作用域中的危险性。

当您将数据存储在 Application 或 Session 作用域时,数据将保留在那里,直到您以编程方式改变它、Session 过期或 Web 应用程序重新启动为止。如果数据需要更新怎么办?要手工强制对 Application 数据进行更新,您可以访问只有管理员才可访问的 ASP 页来更新数据。或者,您可以通过函数定期自动刷新数据。下面例子存储带有缓存数据的时间戳,并隔一段时间后刷新数据。

<%
' error handing not shown...
Const UPDATE_INTERVAL = 300 ' Refresh interval, in seconds

' Function to return the employment status list
Function GetEmploymentStatusList
UpdateEmploymentStatus
GetEmploymentStatusList = Application(?EmploymentStatusList?)
End Function

' Periodically update the cached data
Sub UpdateEmploymentStatusList
Dim d, strLastUpdate
strLastUpdate = Application(?LastUpdate?)
If (strLastUpdate = ??) Or _
(UPDATE_INTERVAL < DateDiff(?s?, strLastUpdate, Now)) Then

' Note: two or more calls might get in here. This is okay and will simply
' result in a few unnecessary fetches (there is a workaround for this)

' FetchEmploymentStatusList function (not shown)
' fetches data from DB, returns an Array
d = FetchEmploymentStatusList()

' Update the Application object. Use Application.Lock()
' to ensure consistent data
Application.Lock
Application(?EmploymentStatusList?) = Events
Application(?LastUpdate?) = CStr(Now)
Application.Unlock
End If
End Sub


请参见 World's Fastest ListBox with Application Data,上面还有一个例子。

要知道在 Session 或 Application 对象中缓存大的数组不是一个好的做法。在访问数组的任何元素之前,脚本语言的语法要求必须临时复制整个数组。例如,如果将由字符串组成的有 100,000 个元素的数组(该数组将美国邮政编码映射到当地的气象站)缓存在 Application 对象中,ASP 必须先将所有的 100,000 个气象站复制到临时数组中,然后才能提取一个字符串。在这种情况下,用自定义方法建立一个自定义组件来存储气象站 - 或使用一个词典组件会更好。

再警告大家一下,不要将婴儿与洗澡水一起倒掉:数组能快速查寻和存储在内存中是邻近的关键数据对。索引一个词典比索引一个数组要慢得多。应针对您的实际情况,选择提供最佳性能的数据结构。

本文作者:
« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3