日期:2010-08-11 浏览次数:20494 次
作者:Rob Howard 译:寒带鱼
这篇文章讨论了:
·一般ASP.NET性能的秘密
·能提高ASP.NET表现的有用的技巧和窍门
·在ASP.NET中使用数据库的建议
·ASP.NET中的缓存和后台处理
使用ASP.NET编写一个Web应用程序是难以置信的简单的。太简单了,以至于很多开发者都不花费时间来构建他们的应用程序来达到很好的表现。在这篇文章里,我将为编写高性能的Web应用程序推荐10个技巧。我不会讲我的论述局限于ASP.NET应用程序,因为ASP.NET应用程序只是Web应用程序的一个子集而已。这篇文章不会是针对优化Web应用程序的性能的权威性指导——一本完整的书可以很容易的做到这一点。相反,我们应该把这篇文章当成一个好的起点。
在成为一个工作狂以前,我会经常去攀岩。在做任何攀岩活动之前,我更愿意看看旅行指南里面的路线,再读读那些曾经到过峰顶的人做的推荐。但是,不管旅行指南写的有多好,在尝试一个有挑战性的目标之前,您都需要有实际的攀岩经验。与之相似,当您面临修复性能问题或者运行一个高吞吐量站点的问题时,您只能学习如何编写高性能 Web 应用程序。
我的个人经验来自在微软的ASP.NET团队中担任过一名基础程序经理的经历,维护和管理www.asp.net,还有帮助构架Community Server,它是几个著名的ASP.NET应用程序(ASP.NET Forums,.Text,和连接到一个平台的nGallery)的下一个版本。我相信这些曾经帮助过我技巧中的一些也会对您有用的。
您应该考虑把您的应用程序分离为几个逻辑层次。您可能已经听说过3层(或者n层)体系结构。这些通常都是规定的结构模式,它们将业务和(或)硬件从物理上进行了功能划分。如果系统需要更大的规模,更多的硬件可以轻松的加进来。然而,那会产生一个与业务和机器跳跃相关联的性能下降,因此我们应该避免它。所以只要可能,尽量在同一个应用程序中运行ASP.NET页面和页面的相关组件。
因为代码的分离和层次之间的边界,使用Web服务或者远程处理会降低性能20%甚至更多。
数据层有点与众不同,因为通常情况下,最好具有专用于数据库的硬件。然而,然而进程跳跃到数据库的成本依然很高,因此在数据层的性能是您优化代码时应该首先考虑的。
在投入到修复您的应用程序的性能问题之前,确保您要先分析您的应用程序来发现问题的根源所在。关键性能计数器(例如那个指示在执行垃圾收集过程中花费的时间百分比的计数器)在找出应用程序在哪里花费了主要的时间时也是非常有用的。虽然那些花费时间的地方经常是不那么直观的。
在这篇文章中我讨论了两种改进性能的方法:大块的优化,例如使用ASP.NET缓存,还有小块的优化,它们经常重复出现。这些小块的优化有时是最有意思的。您对代码的一个小的修改会被调用成千上万次。对大块的优化,您可能会发现整个的性能有了一个大的飞跃。对小块的优化,您可能会缩减了对一个给定请求的几微秒的时间,但是如果把每天的所有的请求累积起来,性能就会得到一个意想不到的改进。
数据层中的性能
当您要开始优化一个应用程序的性能的时候,有一个决定性的测试您可以优先考虑使用:代码是否要访问数据库?如果是,多长时间访问一次?注意这个测试也可以应用到那些使用Web服务或者远程控制的代码中,但是我不会在这篇文章中涉及那些内容。
如果在您的代码中的某个代码路径中要求一个数据库请求,而您发现其他地方您想要优先优化,例如字符串操作,那么停下来然后先执行关键性的测试。除非您有一个性能实在糟糕的问题要处理,否则您的时间会得到更好的利用,如果您把时间花在优化数据库连接的时间,返回的数据量,还有您作的往返数据库的操作中。
现在我已经总体介绍了相关的信息,下面让我们看看10条帮您的应用程序表现更好的技巧。我会从那些对改善性能效果最明显的地方开始说。
技巧 1——返回多个结果集
查看一下您的数据库代码,看看您是否有访问数据库多于一次的请求路径(request paths)。每个这样的往返都回降低您的应用程序每秒可以服务的请求的数量。通过在一次数据库请求中返回多个结果集,您可以减少数据库通信消耗的总时间。在您减少了数据库服务器管理的请求之后,您也会使您的系统更具可升级性。
一般您可以使用动态SQL语句来返回多个结果集,我更喜欢用存储过程。是否应该把业务逻辑放在存储过程中是存在争议的,但我认为如果一个存储过程中的逻辑可以限制返回的数据(减少数据集的大小,花在网络连接上的时间,并且不需要过滤逻辑层的数据),那它就是好东西。
使用一个SqlCommand实例和它的ExecuteReader方法来生成强类型的业务类,您可以通过调用NextResult让结果集指针向前移动。图1展示了一个使用定义的类生成几个ArrayList的示例会话。只从数据库返回您需要的数据会显著地减少您服务器上的内存申请。
1// read the first resultset2reader = command.ExecuteReader();34// read the data from that resultset5while (reader.Read()) {6 suppliers.Add(PopulateSupplierFromIDataReader( reader ));7}89// read the next resultset
10reader.NextResult();
11
12// read the data from that second resultset
13while (reader.Read()) {
14 products.Add(PopulateProductFromIDataReader( reader ));
15}
16
17
技巧 2——分页数据访问
ASP.NET的DataGrid提供了一个非常棒的能力:对数据分页的支持。当在DataGrid中设置了分页,那么将一次显示一个特定数目的结果。此外,用来在结果之间导航的分页UI也会在DataGrid的底部显示出来。分页UI允许您在显示的数据之间向前导航或者向后导航,每页显示特定数目的结果。
但是有一个小问题。使用DataGrid分页时需要所有的数据都绑定到表格。例如,您的数据层会需要返回所有数据,然后DataGrid要根据当前页填充所有要显示的记录。如果当您在使用DataGrid分页时返回了100,000条记录,每次请求都会丢弃99,975条记录(假设每页的容量是25条记录)。当记录的数量不断增长时,应用程序的性能会受到很大的影响,因为每次请求都必须返回越来越多的数据。
一个写出更好的分页代码的办法是使用存储过程。图2显示了一个示例存储过程,它为Nothwind数据库中的Orders数据表分页。总的来说,在这里所有您需要做的就是传入页的索引和页的容量。数据库会计算出适当的结果集然后返回它们。
1CREATE PROCEDURE northwind_OrdersPaged
2(
3 @PageIndex int,
4 @PageSize int
5)
6AS7BEGIN
8DECLARE @PageLowerBound int
9DECLARE @PageUpperBound int
10DECLARE @RowsToReturn int
11
12-- First set the rowcount
13SET @RowsToReturn = @PageSize * (@PageIndex + 1)
14SET ROWCOUNT @RowsToReturn
15
16-- Set the page bounds
17SET @PageLowerBound = @PageSize * @PageIndex
18SET @PageUpperBound = @PageLowerBound + @PageSize + 1
19
20-- Create a temp table to store the select results
21CREATE TABLE #PageIndex
22(
23 IndexId int IDENTITY (1, 1) NOT NULL,
24 OrderID int
25)
26
27-- Insert into the