Len Cardinal - Microsoft Consulting Services 高级顾问 George V. Reilly - Microsoft IIS Performance 主管 更新时间:2000年4月 根据 Nancy Cluts 的文章(英文)改写 Nancy Cluts - 开发人员技术工程师 Microsoft Corporation
性能是一个特性。您需要预先设计性能,或是在日后重新编写应用程序。换句话说,什么是最大限度优化 Active Server Pages (ASP) 应用程序性能的好策略?
本文为优化 ASP 应用程序和“Visual Basic(R) 脚本编辑器 (VBScript)”提供了许多技巧。对许多陷阱和缺陷进行了讨论。本文所列的建议均在 http://www.microsoft.com 及其他站点上进行了测试,而且工作正常。本文假定您对 ASP 开发有基本的理解,包括对 VBScript 和/或 JScript、ASP Application、ASP Session 和其他 ASP 内部对象(请求、响应和服务器)。
ASP 的性能,通常不止取决于 ASP 代码本身。我们并不想在一篇文章中囊括所有的至理名言,只在最后列出与性能相关的资源。这些链接包括 ASP 和非 ASP 主题,包括“ActiveX(R) 数据对象 (ADO)”、“部件对象模型 (COM)”、数据库和“Internet 信息服务器 (IIS)”配置。这些是我们喜欢的链接 - 务请关注它们。
技巧 1:在 Web 服务器上缓存常用数据
典型的 ASP 页从后端数据库检索数据,然后将结果转换为超文本标记语言 (HTML)。无论数据库的速度如何,从内存检索数据要比从后端数据库检索数据快得多。从本地硬盘读取数据通常也要比从数据库检索数据快得多。因此,通常可以通过在 Web 服务器(在内存或磁盘)上缓存数据来改善性能。
<% Function GetEmploymentStatusList Dim d d = Application("EmploymentStatusList") If d = "" Then ' FetchEmploymentStatusList 函数(不显示) ' 从 DB 中取出数据,返回数组 d = FetchEmploymentStatusList() Application("EmploymentStatusList") = d End If GetEmploymentStatusList = d End Function %>
' 取记录集,以数组返回 Function FetchEmploymentStatusList Dim rs Set rs = CreateObject("ADODB.Recordset") rs.Open "select StatusName, StatusID from EmployeeStatus", _ "dsn=employees;uid=sa;pwd=;" FetchEmploymentStatusList = rs.GetRows() ' 以数组返回数据 rs.Close Set rs = Nothing End Function
对上面示例的进一步改进应当是缓存该列表的 HTML,而不是缓存数组。下面是一个简单的范例:
' 取记录集,以“HTML 选项”列表返回 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 字段绑定 Do Until rs.EOF ' 下面一行违背了不要进行字符串连接, ' 但这是可以的,因为我们正在建立高速缓存 s = s & " <option>" & fldName & "</option>" & vbCrLf rs.MoveNext Loop s = s & "</select>" & vbCrLf rs.Close Set rs = Nothing ' 参见尽早释放 FetchEmploymentStatusList = s ' 以字符串返回数据 End Function
如果在 Application 或 Session 作用域中存储数据,这些数据将一直保留在那儿,直到在程序中改变它、Session 过期或 Web 应用程序重新启动时为止。数据需要更新如何处理?若要用手工强制更新应用程序数据,可以调用只允许管理员访问的数据更新 ASP 页。另外,还可以通过函数,周期地自动刷新数据。下面的示例存储带缓存数据的时间戳,在指定时间间隔后刷新数据。
' 函数返回雇佣状态列表 Function GetEmploymentStatusList UpdateEmploymentStatus GetEmploymentStatusList = Application("EmploymentStatusList") End Function
' 定期更新缓存的数据 Sub UpdateEmploymentStatusList Dim d, strLastUpdate strLastUpdate = Application("LastUpdate") If (strLastUpdate = "") Or _ (UPDATE_INTERVAL DateDiff("s", strLastUpdate, Now)) Then
' FetchEmploymentStatusList 函数(不显示) ' 从 DB 中取数据,返回一个数组 d = FetchEmploymentStatusList()
' 更新 Application 对象。用 Application.Lock() ' 来确保一致的数据 Application.Lock Application("EmploymentStatusList") = d Application("LastUpdate") = CStr(Now) Application.Unlock End If End Sub
有时,数据过多不能在内存中进行缓存。“过多”是一种定性的判断;它取决于打算消耗的内存量,还有缓存项的数量和这些项的检索频率。总之,如果有过多的数据要在内存中缓存,请考虑以文本或 XML 文件的形式,在 Web 服务器的硬盘上缓存数据。可以将在磁盘上缓存数据和在内存中缓存数据组合起来,为站点建立最优的缓存策略。
注意,在度量单个 ASP 页的性能时,在磁盘上检索数据不一定比从数据库中检索数据快。但是,缓存减轻了数据库和网络的负荷。在高负荷情况下,这将明显提高总体通信量。在查询成本很高时缓存查询的结果,缓存便非常有效,例如多表联合或复杂的存储过程,或缓存大型的结果集。按照惯例,测试竞争方案。
ASP 和 COM 提供了几种构建磁盘缓存方案的工具。ADO 记录集的 Save() 和 Open() 函数,保存和加载磁盘上的记录集。您可以使用这些方法重写上面 Application 数据缓存技巧中的范例代码,用 Save() 文件替换向 Application 对象写入数据的代码。
还有其他一些处理文件的组件:
Scripting.FileSystemObject 使您能够创建、读取和写入文件。 MSXML 是随 Internet Explorer 提供的 Microsoft(R) XML 解析器,它支持保存和加载 XML 文档。 LookupTable 对象(在 MSN 上使用的范例)是从磁盘加载简单列表的良好选择。 最后,请考虑在磁盘上缓存数据的表示,而不是数据本身。预制的 HTML 可以作为 .htm 或 .asp 文件存储在磁盘上;超级链接可以直接指向这些文件。可以使用商业工具,如 XBuilder 或 Microsoft(R) SQL Server 的 Internet 发行功能来自动化 HTML 生成过程。另外,可以将 HTML 片段 #include 到 .asp 文件。还可以使用 FileSystemObject 从磁盘读取 HTML 文件或使用 XML 进行早期调整(英文)。
技巧 4:避免在 Application 或 Session 对象中缓存非灵活组件
虽然在 Application 或 Session 对象中缓存数据是个好主意,但是缓存 COM 对象可能有严重缺陷。将常用 COM 对象嵌入 Application 或 Session 对象通常具有吸引力。遗憾的是,很多 COM 对象,包括用 Visual Basic 6.0 或更早版本编写的 COM 对象,在 Application 或 Session 对象中存储时将导致严重的瓶颈。