性能概述
更新:2007 年 11 月
性能是成功网站或项目的一个关键因素。本主题提供用于提高网站性能的准则以及指向最佳做法文档的链接。
本主题包括:
最佳做法
背景
代码示例
最佳做法
下面的列表提供了指向 Microsoft 网站上的资源的链接,这些资源介绍 ASP.NET 网站性能的一般最佳做法。
Chapter 4 — Architecture and Design Review of a .NET Application for Performance and Scalability(第 4 章 — 针对性能和可伸缩性的 .NET 应用程序的体系结构和设计检查)
Chapter 6 — Improving ASP.NET Performance(第 6 章 — 提高 ASP.NET 性能)
Microsoft Patterns and Practices(Microsoft 模式和实践)
返回页首
背景
使用本节列出的方法有助于最大程度地提高 ASP.NET Web 应用程序的性能。这些准则分为以下部分:
页面和服务器控件处理
状态管理
数据访问
Web 应用程序
编码实践
页面和服务器控件处理
下列准则提供了有效使用 ASP.NET 页面和控件的建议。
避免不必要的到服务器的往返行程 在某些情况下,可以使用 ASP.NET AJAX 和部分页呈现功能来完成浏览器代码中的任务,而无需执行完全回发。例如,可以先使用 ASP.NET AJAX 功能来验证浏览器中的用户输入,然后再将输入提交到服务器。有关更多信息,请参见 ASP.NET AJAX 概述和部分页呈现概述。
通常,如果不需要将信息传递到服务器以进行验证或以将其写入数据存储区,则可以通过避免到服务器的往返行程来提高页的性能(从而改善用户体验)。
如果开发自定义服务器控件,请考虑将它们设计为针对它们的某些功能来呈现客户端脚本。这样可以显著减少向 Web 服务器发送信息的次数。有关更多信息,请参见开发自定义 ASP.NET 服务器控件和使用 Microsoft AJAX Library 创建自定义客户端脚本。
使用 Page 对象的 IsPostBack 属性来避免不必要的处理 如果只是在首次请求页时必须运行代码,请避免在每次回发时都运行代码。根据页是否是为了响应服务器控件事件才运行的,可以测试 IsPostBack 属性以有条件地执行代码。
除非有特殊的原因要关闭缓冲,否则使其保持打开状态 禁用 ASP.NET 网页的缓冲会导致大量的性能开销。有关更多信息,请参见 Buffer 属性。
使用 Server 对象的 Transfer 方法或使用跨页发送在同一应用程序中的不同 ASP.NET 页之间重定向 有关更多信息,请参见将用户重定向到另一页。
状态管理
下列准则提供了有效进行状态管理的建议。
仅在必要时才保存服务器控件视图状态 视图状态使服务器控件可以在往返行程中重新填充属性值,而无需您编写代码。但是,由于在隐藏形式的字段中,视图状态会传入和传出服务器,因此它会影响性能和页大小。如果在每个往返行程中将服务器控件绑定到数据,由于在数据绑定期间会用新值替换控件的值,因此保存的视图状态也没什么用处。在这种情况下,禁用视图状态可以节省处理时间并减少页的大小。
默认情况下,已对所有服务器控件启用了视图状态。若要对某个控件禁用它,请将该控件的 EnableViewState 属性设置为 false,如下面的示例所示:
<asp:datagrid EnableViewState="false" datasource="..." runat="server"/>
还可以使用 @ Page 指令对某个页禁用视图状态,如下面的示例所示:
<%@ Page EnableViewState="false" %>
这在页不需要回发处理时十分有用。
说明:
@ Control 指令中还支持 EnableViewState 属性以指定是否为用户控件启用视图状态。
若要分析某个页的视图状态大小,请通过在 @ Page 指令中设置 trace="true" 来启用对该页的跟踪。然后,在跟踪输出中检查**“控件层次结构”表的“Viewstate”**列。有关更多信息,请参见 ASP.NET 跟踪概述。
除非必要,否则避免使用视图状态加密功能 视图状态加密功能可阻止用户读取隐藏视图状态形式的字段中的视图状态值。例如,如果页中包括一个 GridView 控件,该控件维护 DataKeyNames 属性中的标识符字段以协调对记录的更新,则可能需要加密视图状态。由于不希望用户看到标识符,因此可以加密视图状态。但是,加密对于初始化具有恒定的性能开销,并具有取决于要加密的视图状态大小的附加开销。每次加载页时都会执行加密。因此,首次请求页时以及每次回发时都会产生相同的性能影响。
当不使用会话状态时禁用它 若要对某个页禁用会话状态,请将 @ Page 指令中的 EnableSessionState 属性设置为 false,如下面的示例所示:
<%@ Page EnableSessionState="false" %>
说明:
如果页需要访问会话变量,但不会创建或修改它们,则将 @ Page 指令中的 EnableSessionState 属性设置为 ReadOnly。
还可以禁用 ASP.NET Web 服务方法的会话状态。有关更多信息,请参见 ASP.NET 应用程序服务概述。
若要禁用应用程序的会话状态,请在应用程序的 Web.config 文件的 SessionState 节中将 Mode 属性设置为 Off,如下面的示例所示:
<sessionState mode="Off" />
为应用程序选择合适的会话状态提供程序 ASP.NET 提供了多种方法用来存储应用程序的会话数据。其中包括进程内会话状态、作为 Windows 服务的进程外会话状态以及 SQL Server 数据库中进程外会话状态。(还可以创建自定义会话状态提供程序,以在指定的数据存储区中存储会话数据。)每种方法都有自己的优点,但进程内会话状态是目前为止速度最快的方法。如果仅对会话状态中的少量数据使用会话状态,请使用进程内提供程序。在以下情况下,请使用进程外会话状态选项:跨多个处理器或多台计算机缩放应用程序,或者希望在服务器或进程重新启动时保留会话数据。有关更多信息,请参见 ASP.NET 会话状态概述。
数据访问
下列准则提供了在应用程序中有效进行数据访问的建议。
将 SQL Server 和存储过程用于数据访问 SQL Server 是创建高性能、可缩放 Web 应用程序的数据存储推荐选择。使用托管 SQL Server 提供程序时,可通过尽可能使用编译的存储过程而不是使用 SQL 命令来获得额外的性能提升。有关信息,请参见配置参数 (ADO.NET)。
将 SqlDataReader 类用于快速仅向前数据游标 SqlDataReader 类创建从 SQL Server 数据库检索的仅向前、只读数据流。SqlDataReader 类使用 SQL Server 本机网络数据传输格式直接从数据库连接读取数据。如果可行,请使用 SqlDataReader 类,因为与 DataSet 类相比,它可以提供更优的性能。例如,将数据控件绑定到 SqlDataSource 控件时,如果将 DataSourceMode 属性设置为 DataReader,您会得到更优的性能。(但是,与 DataSet 模式相比,数据读取器支持的功能比较少。)SqlDataReader 类实现 IEnumerable 接口,使您能够将服务器控件绑定到该类。有关更多信息,请参见 SqlDataReader 类和通过 ASP.NET 访问数据。
尽可能缓存数据和页输出 如果必须为每个页请求动态计算页或数据,请使用 ASP.NET 缓存。如有可能,请针对缓存设计页和数据请求,尤其是预期有较大通讯量时更应执行此操作。与使用 .NET Framework 的任何其他功能相比,适当地使用缓存可以更好地提高站点的性能。
使用 ASP.NET 缓存时,请遵循下列准则。首先,不要缓存太多项,因为每个缓存项都需要占用服务器内存。例如,不要缓存容易重新计算或很少使用的项。其次,给缓存项分配的有效期不要太短。很快到期的项会导致额外的代码清除和垃圾回收工作。使用与 ASP.NET Applications 性能对象相关联的 Cache Total Turnover Rate(缓存总流通率)性能计数器,可以监视缓存中由于项到期而导致的周转。高周转率可能说明存在问题,特别是当项在到期前被移除时。(这种情况有时称作内存压力。)
有关如何缓存页输出和数据请求的信息,请参见 ASP.NET 缓存概述。
适当地使用 SQL 缓存依赖项 对于 SQL Server 中的缓存数据,ASP.NET 同时支持基于表的轮询和查询通知,具体取决于所使用的 SQL Server 的版本。所有 SQL Server 版本都支持基于表的轮询。在基于表的轮询中,如果某个表中的任何数据发生更改,与该表有关的所有缓存项都会失效。这会导致缓存中不必要的周转。建议不要对经常发生变化的表使用基于表的轮询。例如,建议对很少发生变化的目录表使用基于表的轮询。建议不要对经常进行更新的订单表使用基于表的轮询。
SQL Server 2005 及更高版本支持查询通知。查询通知使用 SQL 查询来检测目标行集中的更改。这样可以减少表发生变化时发送的通知数。与基于表的轮询相比,查询通知可以提供更优的性能。但是,不能将它扩展到适用于数千个查询。
有关 SQL 缓存依赖项的更多信息,请参见演练:将 ASP.NET 输出缓存与 SQL Server 结合使用和使用 SqlCacheDependency 类在 ASP.NET 中缓存。
使用数据源分页和排序而不是 UI(用户界面)分页和排序 DetailsView 和 GridView 等数据控件的 UI 分页功能可用于支持 ICollection 接口的任何数据源对象。对于每个分页操作,数据控件会查询数据源中的整个数据集合,选择要显示的行,并放弃其余的数据。
如果某个数据源控件实现 DataSourceView 并且 CanPage 属性返回 true,该数据控件会使用数据源分页而不是 UI 分页。在这种情况下,该数据控件仅请求要显示的每个页所需的行。因此,数据源分页比 UI 分页高效。在标准 ASP.NET 控件中,只有 ObjectDataSource 和 LinqDataSource 数据源控件支持数据源分页。若要对其他数据源控件启用数据源分页,可以从要使用的数据源控件继承,然后修改其行为。
**将并发控件的 Timestamp 列用于 LinqDataSource 控件 **如果 SQL Server 数据库表不包含 Timestamp 列(一种 SQL Server 数据类型),LinqDataSource 控件会通过将原始数据值存储在网页中来检查数据并发。LINQ to SQL 在更新或删除数据之前根据数据库对原始值进行检查。此方法能够在数据记录包含很多列或大量列值的情况下创建大型网页。如果该记录包含您不想在页面中公开的数据,则这种方法还会带来安全风险。当数据库表有一个 Timestamp 列时,LinqDataSource 控件会仅存储时间戳值,以供将来进行比较。通过确定原始时间戳与表中的当前时间戳值是否匹配,LINQ to SQL 可以检查数据的一致性。有关时间戳的更多信息,请参见 MSDN 网站上的 timestamp (Transact-SQL)。
平衡事件验证的安全性受益和性能开销 从 System.Web.UI.WebControls 和 System.Web.UI.HtmlControls 类派生的控件可以验证事件是否源自该控件所呈现的用户界面。这样有助于防止控件响应伪造的事件通知。例如,通过使用事件验证,DetailsView 控件可以防止恶意用户进行 Delete 调用(控件本身不支持该调用)以及操作控件来删除数据。事件验证具有一定的性能开销。可以使用 EnableEventValidation 配置元素和 RegisterForEventValidation 方法来控制事件验证。验证的开销取决于页上的控件数量,并在几个百分点范围内。
安全说明:
强烈建议不要禁用事件验证。在禁用事件验证之前,请确保不会构造出会对应用程序产生意外影响的回发。
使用 SqlDataSource 控件进行缓存、排序和筛选 如果 SqlDataSource 控件的 DataSourceMode 属性设置为 DataSet,则 SqlDataSource 控件可以缓存查询的结果集。然后,SqlDataSource 控件的筛选和排序操作可以使用缓存的数据。如果缓存整个数据集并使用 FilterExpression 和 SortParameterName 属性进行排序和筛选,应用程序会运行得更快。然后,数据源控件可以避免进行带 Where 和 Sort By 子句的 SQL 查询,以便用户每次在 UI 中对数据进行排序或筛选时访问数据库。
Web 应用程序
下列准则提供了可使整个 Web 应用程序有效工作的建议。
预编译站点 首次请求资源(如 ASP.NET 网页)时,Web 应用程序将进行批编译。如果应用程序中的页都没有编译,批编译功能会成批编译目录中的所有页,以增加磁盘和内存的使用率。可以使用 ASP.NET 编译工具 (Aspnet_compiler.exe) 来预编译 Web 应用程序。对于就地编译,该编译工具会调用 ASP.NET 运行时来编译站点,其方式与用户向网站请求页时的方式相同。可以预编译 Web 应用程序,以便保留 UI 标记;也可以预编译页,以便不能更改源代码。有关更多信息,请参见如何:预编译 ASP.NET 网站。
禁用调试模式 在部署生产应用程序或进行任何性能度量之前,请始终禁用调试模式。如果启用调试模式,可能会降低应用程序的性能。有关如何设置调试模式的信息,请参见编辑 ASP.NET 配置文件。
优化 Web 服务器计算机和特定应用程序的配置文件 ASP.NET 的默认配置会启用最广泛的功能集和最常见的方案。可以更改某些默认配置设置,以提高应用程序的性能,具体取决于所使用的功能。下面的列表包括您应考虑的配置更改:
仅对需要的应用程序启用身份验证 默认情况下,ASP.NET 应用程序的身份验证模式为 Windows 或集成的 NTLM。大多数情况下,最好仅对需要身份验证的应用程序在 Machine.config 文件中禁用身份验证,并在 Web.config 文件中启用身份验证。
使用适当的请求和响应编码设置来配置应用程序 ASP.NET 默认编码格式为 UTF-8。如果您的应用程序仅使用 ASCII 字符,请针对 ASCII 配置您的应用程序,以获得稍许的性能提高。
对应用程序禁用 AutoEventWireup 在 Web.config 文件中将 AutoEventWireup 属性设置为 false,可以防止页将页事件绑定到基于名称匹配的方法(例如 Page_Load)。这会略微提高页的性能。若要处理页事件,请使用以下两种策略之一。第一种策略是重写方法。例如,可以重写 Page 对象的 OnLoad 方法,编写页加载事件的代码。(请务必调用基方法以确保引发所有事件。)第二种策略是在 Visual Basic 中使用 Handles 关键字来绑定到页事件,或者在 C# 中使用委托连接来绑定到页事件。
从请求处理管线中移除未使用的模块 默认情况下,Web 服务器计算机的 Machine.config 文件中 HttpModules 节点的所有功能均处于活动状态。根据应用程序所使用的功能,可以从请求管线中移除未使用的模块,以获得稍许的性能提高。检查每个模块及其功能,并按您的需要自定义它。例如,如果在应用程序中不使用会话状态和输出缓存,则可以从 HttpModules 列表中移除这些功能的模块。
在 Internet 信息服务 5.0 上进程外运行 Web 应用程序 默认情况下,IIS 5.0 上的 ASP.NET 将使用进程外辅助进程为请求提供服务。此功能已被优化以提高吞吐量。由于在进程外的辅助进程中运行 ASP.NET 有其功能和优点,建议在生产站点上使用它。
定期回收进程 为了同时保证稳定性和性能,应该定期回收进程。经过较长的时间,有内存泄漏和 Bug 的资源可以影响 Web 服务器的吞吐量,而回收进程可以清理内存避免这类问题。但是,应该平衡定期回收的需求和过频的回收,因为停止辅助进程、重新加载页面以及重新获取资源和数据的开销可能会超过回收带来的好处。
在 Windows Server 2003 和 IIS 6.0 上运行的 ASP.NET Web 应用程序不需要调整进程模型,因为 ASP.NET 将使用 IIS 6.0 进程模型设置。
为应用程序正确设置每个辅助进程的线程数 ASP.NET 的请求体系结构会尝试在执行请求的线程数和可用资源之间达到一种平衡。该体系结构将根据可用于请求的 CPU 资源来决定允许同时执行的请求数。这项技术称作线程门控。但是在某些条件下,线程门控算法不是很有效。通过使用与 ASP.NET Applications 性能对象相关联的 Pipeline Instance Count(管线实例计数)性能计数器,可以在 Windows 性能监视器中监视线程门控。
当 ASP.NET 网页调用外部资源时(例如,访问数据库或处理 ASP.NET Web 服务请求时),页请求通常会停止,直到外部资源响应为止。这将释放 CPU 以处理其他线程。如果另一个请求正在等待处理,并且线程池中有一个线程释放,则开始处理这个正在等待的请求。结果可导致 ASP.NET 辅助进程或应用程序池中存在许多同时执行的请求和许多正在等待的线程。这会影响 Web 服务器的吞吐量,从而对性能产生不利的影响。
为了减少对性能的这种影响,可以对进程中的线程数手动设置限制。为此,可以更改 Machine.config 文件中 processModel 节中的 MaxWorkerThreads 和 MaxIOThreads 属性。
说明:
辅助线程用来处理 ASP.NET 请求。IO 线程用于为来自文件、数据库或 ASP.NET Web 服务的数据提供服务。
分配给进程模型属性的值表示进程中每个 CPU 每类线程的最大数目。对于双处理器计算机,最大数是设置值的两倍。对于四处理器计算机,最大值是设置值的四倍。对于有一个和两个处理器的计算机,使用默认设置即可。但是,对于有两个以上处理器的计算机,如果进程中有 100 或 200 个线程,会降低性能。如果进程中有太多线程,则会降低速度。服务器必须执行额外的上下文交换,这会导致操作系统将 CPU 周期花在维护线程而不是处理请求上。可以通过对应用程序进行性能测试来确定适当的线程数目。
对于广泛依赖外部资源的应用程序,请在多处理器计算机上启用网络园艺 ASP.NET 进程模型可帮助启用多处理器计算机上的可伸缩性,方法是将工作分发给多个进程(每个 CPU 一个),并且每个进程都将处理器关联设置为一个 CPU。这种技术有时称为“网络园艺”。如果 Web 应用程序广泛使用外部资源,则它们可以从网络园艺中受益。例如,如果应用程序使用数据库服务器或调用具有外部依赖项的 COM 对象,则应用程序可以受益。但是,在对生产网站启用网络园艺之前,应测试 Web 应用程序在网络园艺方面的性能。
编码实践
下列准则提供了编写有效代码的建议。
不要依赖代码中的异常 异常会大大地降低性能。因此,应避免将它们用作控制正常程序流的方式。如有可能,请在代码中包括用于检测和处理可能导致异常的条件的逻辑。常见的代码检测方案包括:检查 null、将字符串分析为数值以及在应用数学运算前检查特定值。以下示例演示可能导致异常的代码,以及测试某个条件是否存在的备用代码。两个示例产生相同的结果。
// This is not recommended. try { result = 100 / num; } catch (Exception e) { result = 0; } // This is preferred. if (num != 0) result = 100 / num; else result = 0;
' This is not recommended. Try result = 100 / num Catch (e As Exception) result = 0 End Try ' This is preferred. If Not (num = 0) result = 100 / num Else result = 0 End If
代码示例
帮助和演练主题
演练:将 ASP.NET 输出缓存与 SQL Server 结合使用
返回页首
请参见
概念
参考
返回页首