ASP.NET程序性能优化的七个方面
一、数据库操作
1、用完马上关闭数据库连接
访问数据库资源需要创建连接、打开连接和关闭连接几个操作。这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资
源。ASP.NET中提供了连接池(Connection Pool)改善打开和关闭数据库对性能的影响。系统将用户的数据库连接放在连接池中,需要时取出,关闭时收回连接,等待下一次的连接请求。
连接池的大小是有限的,如果在连接池达到最大限度后仍要求创建连接,必然大大影响性能。因此,在建立数据库连接后只有在真正需要操作时才打开连接,使用完毕后马上关闭,从而尽量减少数据库连接打开的时间,避免出现超出连接限制的情况。
用(推荐)
using(SqlConnection Conn=new
SqlConnection(connstr))
{}//不必显示关闭
或
try{conn.Open();}
catch{}
finally{conn.Close();}
2、尽量使用存储过程,并优化查询语句
存储过程是存储在服务器上的一组预编译的SQL语句,类似于DOS系统中的批处理文件。存储过程具有对数据库立即访问的功能,信息处理极为迅速。使用存储过程可以避免对命令的多次编译,在执行一次后其执行规划就驻留在高速缓存中,以后需要时只需直接调用缓存中的二进制代码即可。在
.NET Framework 提供的所有数据访问方法中,基于 SQL Server 的数据访问是生成高性能、可缩放 Web 应用程序的推荐选择。使用托管 SQL
Server
提供程序时,可通过使用编译的存储过程而不是特殊查询获得额外的性能提高。
另外,存储过程在服务器端运行,独立于ASP.NET程序,便于修改,最重要的是它可以减少数据库操作语句在网络中的传输。
优化查询语句
ASP.NET中ADO连接消耗的资源相当大,SQL语句运行的时间越长,占用系统资源的时间也越长。因此,尽量使用优化过的SQL语句以减少执行时间。比如,不在查询语句中包含子查询语句,尽量只返回有用的数据、字段,充分利用索引等。
3、只读数据访问用SqlDataReader,不要使用DataSet
SqlDataReader 类提供了一种读取从 SQL Server 数据库检索的只进数据流的方法。如果当创建 ASP.NET
应用程序时出现允许您使用它的情况,则 SqlDataReader 类提供比 DataSet 类更高的性能。情况之所以这样,是因为 SqlDataReader
使用 SQL Server 的本机网络数据传输格式从数据库连接直接读取数据。另外,SqlDataReader 类实现 IEnumerable
接口,该接口也允许您将数据绑定到服务器控件。DataSet作为一个功能强大的、支持离线的数据库,其对性能的开销也相对较大。
Sqldataread优点:读取数据非常快。如果对返回的数据不需做大量处理的情况下,建议使用SqlDataReader,其性能要比datset好很多。缺点:直到数据读完才可close掉于数据库的连接。
Dataset是把数据读出,缓存在内存中。缺点:对内存的占用较高。如果对返回的数据需做大量的处理用Dataset比较好些可以减少对数据库的连接操作。优点:只需连接一次就可close于数据库的连接。
一般情况下,读取大量数据,对返回数据不做大量处理用SqlDataReader.对返回数据大量处理用datset比较合适.对SqlDataReader和Dataset的选择取决于程序功能的实现。
4、数据的绑定DataBinder
一般的绑定方法<%# DataBinder.Eval(Container.DataItem, "字段名")
%>
用DataBinder.eval
绑定不必关心数据来源(read或dataset)。不必关心数据的类型eval会把这个数据对象转换为一个字符串。在底层绑定做了很多工作,使用了反射性能。正因为使用方便了,但却影响了数据性能。
来看下<%# DataBinder.Eval(Container.DataItem, "字段名")
%>。当于dataset绑定时,DataItem其实式一个DataRowView(如果绑定的是一个数据读取器(dataread)它就是一个IdataRecord。)因此直接转换成DataRowView的话,将会给性能带来很大提升。
<%#
ctype(Container.DataItem,DataRowView).Row("字段名")
%>
对数据的绑定建议使用<%#
ctype(Container.DataItem,DataRowView).Row("字段名")
%>。使用时注意两个方面:
1.需在页面添加<%@ Import
namespace="System.Data"%>.
2.注意字段名的大小写(要特别注意)。如果和查询的不一致,在某些情况下会导致比<%#
DataBinder.Eval(Container.DataItem, "字段名") %>还要慢。如果想进一步提高速度,可采用<%#
ctype(Container.DataItem,DataRowView).Row(0)
%>的方法。不过其可读性不高。
以上的是vb.net的写法。在c#中:<%# ((DataRowView)Container.DataItem)["字段名"]
%>
5、返回多个结果集
无论SqlDataReader还是datset,返回多个结果集,然后用rd.NextResult()或ds.Tables[i]来分别处理数据,减少重复连接数据库的次数。同时尽量用比较高效的SQL代替后续复杂的DataSet二次加工。
二、页面优化
1、不使用不必要的服务器控件(Server
Control)
ASP.net中,大量的服务器端控件方便了程序开发,但也可能带来性能的损失,因为用户每操作一次服务器端控件,就产生一次与服务器端的往返过程。因此,非必要,应当少使用Server
Control。还有许多其他情况,在这些情况中呈现或数据绑定比使用服务器控件更有效,甚至是在使用服务器控件模板时。但是,如果要以编程方式操作服务器控件的属性、处理服务器控件事件或利用视图状态保存,则使用服务器控件是适当的。
所以,尽量选择html控件。能在客户端实现的功能就在客户端实现(熟练掌握javascript),减少服务器的压力。
2、不使用不必要的ViewState
默认情况下,ASP.Net对所有的Server
Control都启用了ViewState(视图状态)。但ViewState需要在客户端保存一些信息,这会造成性能的消耗。当必须使用Server
Control时,可以考虑禁止ViewState。
只在必要时保存服务器控件视图状态。自动视图状态管理是服务器控件的功能,该功能使服务器控件可以在往返过程上重新填充它们的属性值(您不需要编写任何代码)。但是,因为服务器控件的视图状态在隐藏的窗体字段中往返于服务器,所以该功能确实会对性能产生影响。您应该知道在哪些情况下视图状态会有所帮助,在哪些情况下它影响页的性能。例如,如果您将服务器控件绑定到每个往返过程上的数据,则将用从数据绑定操作获得的新值替换保存的视图状态。在这种情况下,禁用视图状态可以节省处理时间。
默认情况下,为所有服务器控件启用视图状态。若要禁用视图状态,请将控件的
EnableViewState 属性设置为 false,如下面的 DataGrid
服务器控件示例所示。
<asp:datagrid EnableViewState="false"
runat="server"/>
您还可以使用 @ Page 指令禁用整个页的视图状态。当您不从页回发到服务器时,这将十分有用: <%@
Page EnableViewState="false" %>
注意 @ Control 指令中也支持
EnableViewState 属性,该指令允许您控制是否为用户控件启用视图状态。
若要分析页上服务器控件使用的视图状态的数量,请(通过将
trace="true" 属性包括在 @ Page 指令中)启用该页的跟踪并查看 Control Hierarchy 表的 Viewstate
列。
3、避免到服务器的不必要的往返过程
虽然您很可能希望尽量多地使用 Web 窗体页框架的那些节省时间和代码的功能,但在某些情况下却不宜使用 ASP.NET
服务器控件和回发事件处理。
通常,只有在检索或存储数据时,您才需要启动到服务器的往返过程。多数数据操作可在这些往返过程间的客户端上进行。例如,从 HTML
窗体验证用户输入经常可在数据提交到服务器之前在客户端进行。通常,如果不需要将信息传递到服务器以将其存储在数据库中,那么您不应该编写导致往返过程的代码。
如果您开发自定义服务器控件,请考虑让它们为支持 ECMAScript
的浏览器呈现客户端代码。通过以这种方式使用服务器控件,您可以显著地减少信息被不必要的发送到 Web
服务器的次数。
使用
Page.IsPostBack 避免对往返过程执行不必要的处理
如果您编写处理服务器控件回发处理的代码,有时可能需要在首次请求页时执行其他代码,而不是当用户发送包含在该页中的 HTML
窗体时执行的代码。根据该页是否是响应服务器控件事件生成的,使用 Page.IsPostBack
属性有条件地执行代码。例如,下面的代码演示如何创建数据库连接和命令,该命令在首次请求该页时将数据绑定到 DataGrid 服务器控件。
void
Page_Load(Object sender, EventArgs e)
{
if(!Page.IsPostBack)
{}
}
由于每次请求时都执行 Page_Load 事件,上述代码检查 IsPostBack 属性是否设置为
false。如果是,则执行代码。如果该属性设置为true,则不执行代码。
注意如果不运行这种检查,回发页的行为将不更改。Page_Load
事件的代码在执行服务器控件事件之前执行,但只有服务器控件事件的结果才可能在输出页上呈现。如果不运行该检查,仍将为 Page_Load
事件和该页上的任何服务器控件事件执行处理。
4、当不使用会话状态时禁用它,并且程序开发中尽量少用Session
并不是所有的应用程序或页都需要针对于具体用户的会话状态,您应该对任何不需要会话状态的应用程序或页禁用会话状态。
若要禁用页的会话状态,请将 @ Page 指令中的 EnableSessionState 属性设置为 false。例如: <%@ Page
EnableSessionState="false" %>
注意如果页需要访问会话变量,但不打算创建或修改它们,则将 @ Page 指令中的
EnableSessionState 属性设置为 ReadOnly。
若要禁用应用程序的会话状态,请在应用程序 Web.config 文件的 sessionstate 配置节中将 mode 属性设置为
off。例如:<sessionstate mode="off" />
5、合理使用DataGrid(在asp.net2.0中为GridView)控件
DataGrid控件带有最强大的数据显示功能,还内置了对数据的修改、删除、添加、分页等很多功能。如果只需简单的显示数据,
DataGrid并非最佳选择。DataGrid控件的分页功能,数据的存储方式(存储在viewstate中)等,虽然让程序开发者使用方便快捷,但由此产生的性能开销不容小视。
DataList控件比DataGrid功能少了很多。但自定义性强了很多。特有的多行数据显示还是比较方便的。DataGrid能实现的功能,它基本能实现。
Repeater控件功能最少,但自定义性非常强。由于减少了很多功能,对服务器的性能带来消耗最小。
因此,在只需简单显示数据列表时,选择Repeater或DataList控件同样可以达到目的,而且减轻了性能上的开销。
建议选择顺序:Repeater然后DataList最后DataGrid(GridView)
6、对数据进行分页
ASP.NET的DataGrid(在asp.net2.0中为GridView)有一个非常有用的功能:分页。如果DataGrid允许分页,在某一时刻它只下载某一页的数据,另外,它有一个数据分页的浏览导航栏,它让你可以选择浏览某一页,而且每次只下载一页的数据。
但是它有一个小小的缺点,就是你必须把所有的数据都绑定到DataGrid中。也就是说,你的数据层必须返回所有的数据,然后DataGrid再根据当前页过滤出当前页所需要的数据显示出来。如果有一个一万条记录的结果集要用DataGrid进行分页,假设DataGrid每页只显示25条数据,那就意味着每次请求都有9975条数据都是要丢弃的。每次请求都要返回这么大的数据集,对应用程序的性能影响是非常大的。
一个好的解决方案是写一个分页的存储过程,
如:
CREATE
PROCEDURE dbo.sp_DataPages
@Sql nVARCHAR(2000),
@PK varchar(50),
--主键字段名(可以是任何数据类型;不能,并且也不需要带表名前缀,如:不可以是users.id)
@Order varchar(50),
--排序方式(带desc或asc,可以是多个组合;不能,并且也不需要带表名前缀,如:不可以是users.id desc)
@Page
int,
@PageSize int = 30
AS
set nocount
on
DECLARE @Str nVARCHAR(4000)
if(@Page=1)
SET @Str= ‘Select Top ‘+ CAST((@PageSize)
As VARCHAR(20)) + ‘ * From (‘+ @Sql + ‘) As table1 Order By table1.‘ +
@Order
else
SET @Str= ‘Select Top ‘+ CAST((@PageSize) As VARCHAR(20)) + ‘
* From (‘+ @Sql + ‘) As table1 Where table1.‘ + @PK + ‘ Not In (Select Top ‘ +
CAST((@PageSize*(@Page-1)) AS VARCHAR(20)) + ‘ ‘ + @PK + ‘ From (‘ + @Sql + ‘)
As table2 Order By table2.‘ + @Order + ‘ ) Order By table1.‘ +
@Order
--print
@str
--exec (@Str)
EXEC sp_ExecuteSql @Str
set nocount
off
GO
或者,使用sql server
2005中的row_number()函数
declare @sql
varchar(8000)
set
@sql=‘select *‘
+‘ from‘
+‘ (‘
+‘ select
row_number() over (order by id desc) as rowNumber,*‘
+‘ from Users‘
+‘
where id>0 and name<>‘‘‘‘‘
+‘ )‘
+‘
as table1‘
+‘ where
rowNumber between ‘+str((@page-1)*@pagesize+1)+‘ AND
‘+str(@page*@pagesize)
+‘ order by id desc‘
--exec
(@sql)
EXEC sp_ExecuteSql
@sql
(小技巧:将记录总数保为Cache或Session来提高分页性能。)
7、不要禁用 Web 窗体页的缓冲
除非有特殊的原因要关闭缓冲,否则使其保持打开。禁用 Web
窗体页的缓冲会导致大量的性能开销。
启用页面输出的缓冲区(Buffer)
如果Buffer的机制被关闭,可以用下面的方法打开。
使用程序打开页面输出缓存:
Response.BufferOutput
= true;
使用@Page开关打开页面输出缓冲机制:
<%@
Page Buffer = "true" %>
使用Web.config或Machine.config配置文件的<pages>节点:
<pages
buffer="true"/>
8、设置page的smart navigation属性
smart
navigation设置为true能让用户明显的感觉性能提高。启用此属性后对客户端和服务端影响不大.它能智能刷新需要刷新的部分。
在大多数情况下不要在代码中设置该属性。
在 .aspx
文件的 @ Page 指令中将 SmartNavigation 属性设置为
true。请求该页时,动态生成的类将设置该属性。
Internet
Explorer 5 或更高版本浏览器请求页时(或稍后),智能导航将通过执行下列功能提高用户对该页的操作能力:
消除导航导致的闪烁。
从一页移动到另一页时保持滚动位置。
保持导航之间的元素焦点。
在浏览器的历史记录中只保留最后一页的状态。
智能导航最适用于需要频繁回发,但是其内容在返回时不会发生显著更改的 ASP.NET
页。在决定是否将该属性设置为 true 时,请仔细考虑这一点。
三、c#(或vb.net)程序改进
1、使用值类型的ToString方法
在连接字符串时,经常使用"+"号直接将数字添加到字符串中。这种方法虽然简单,也可以得到正确结果,但是由于涉及到不同的数据类型,数字需要通过装箱操作转化为引用类型才可以添加到字符串中。但是装箱操作对性能影响较大,因为在进行这类处理时,将在托管堆中分配一个新的对象,原有的值复制到新创建的对象中。
使用值类型的ToString方法可以避免装箱操作,从而提高应用程序性能。
int
num=1;
string str="go"+num.ToString();
2、运用StringBuilder类
String类对象是不可改变的,对于String对象的重新赋值在本质上是重新创建了一个String对象并将新值赋予该对象,其方法ToString对性能的提高并非很显著。
在处理字符串时,最好使用StringBuilder类,其.NET
命名空间是System.Text。该类并非创建新的对象,而是通过Append,Remove,Insert等方法直接对字符串进行操作,通过ToString方法返回操作结果。
其定义及操作语句如下所示:
int
num;
System.Text.StringBuilder str = new
System.Text.StringBuilder(); //创建字符串
str.Append(num.ToString());
//添加数值num
Response.Write(str.ToString);
//显示操作结果
3、使用 HttpServerUtility.Transfer
方法在同一应用程序的页面间重定向
采用
Server.Transfer
语法,在页面中使用该方法可避免不必要的客户端重定向(Response.Redirect)。
4、避免使用ArrayList。
因为任何对象添加到ArrayList都要封箱为System.Object类型,从ArrayList取出数据时,要拆箱回实际的类型。建议使用自定义的集合类型代替ArrayList。asp.net
2.0提供了一个新的类型,叫泛型,这是一个强类型,使用泛型集合就可以避免了封箱和拆箱的发生,提高了性能。
5、使用HashTale代替其他字典集合类型
(如StringDictionary,NameValueCollection,HybridCollection),存放少量数据的时候可以使用HashTable.
6、为字符串容器声明常量,不要直接把字符封装在双引号"
"里面。
//避免
MyObject obj = new MyObject();
obj.Status =
"ACTIVE";
//推荐
const string C_STATUS = "ACTIVE";
MyObject
obj = new MyObject();
obj.Status = C_STATUS;
7、不要用ToUpper(),ToLower()转换字符串进行比较,用String.Compare代替,它可以忽略大小写进行比较.
例:
const string C_VALUE = "COMPARE";
if
(String.Compare(sVariable, C_VALUE, true) == 0)
{
Console.Write(
"相同");
}
也可以用str == String.Empty或者str.Length ==
0判断是否为空。(注意判断输入数据的长度,可防止sql注入式攻击)
将String对象的Length属性与0比较是最快的方法,避免不必要的调用
ToUpper 或 ToLower 方法。
8、类型转化Int32.TryParse()优于Int32.Parse()优于Convert.ToInt32()。
建议.NET1.1下用Int32.Parse();.NET2.0用Int32.TryParse()。
因为:
Convert.ToInt32
会把最终的解析工作代理给 Int32.Parse;
Int32.Parse
会把最终的解析工作代理给Number.ParseInt32;
Int32.TryParse
会把最终的解析工作代理给Number.TryParseInt32。
9、如果只是从XML对象读取数据,用只读的XPathDocument代替XMLDocument,可以提高性能
//避免
XmlDocument xmld = new
XmlDocument();
xmld.LoadXml(sXML);
txtName.Text = xmld.SelectSingleNode(
"/packet/child").InnerText;
//推荐
XPathDocument xmldContext = new
XPathDocument(new StringReader(oContext.Value));
XPathNavigator xnav =
xmldContext.CreateNavigator();
XPathNodeIterator xpNodeIter = xnav.Select(
"packet/child");
iCount = xpNodeIter.Count;
xpNodeIter =
xnav.SelectDescendants(XPathNodeType.Element, false);
while(xpNodeIter.MoveNext())
{
sCurrValues +=
xpNodeIter.Current.Value+ ",";
}
10、避免在循环体里声明变量,应该在循环体外声明变量,在循环体里初始化。
C#程序开发要遵循的一个基本原则就是避免不必要的对象创建
//避免
for(int
i=0; i<10; i++)
{
SomeClass objSC = new SomeClass();
}
//推荐
SomeClass objSC = null;
for(int i=0; i
<10; i++)
{
objSC = new SomeClass();
}
11、捕获指定的异常,不要使用通用的System.Exception.
//避免
try
{
<some logic>
}
catch(Exception exc)
{
<Error handling>
}
//推荐
try
{
<some logic>
}
catch(System.NullReferenceException exc)
{
<Error
handling>
}
catch(System.ArgumentOutOfRangeException exc)
{
<Error handling>
}
catch(System.InvalidCastException exc)
{
<Error handling>
}
12、使用Try...catch...finally时,
要在finally里释放占用的资源如连接,文件流等
不然在Catch到错误后占用的资源不能释放。
try
{}
catch
{}
finally
{
conntion.close();
}
13、不要用Exception控制程序流程
有些程序员可能会使用异常来实现一些流程控制。例如:
try{
result=100/num;
}
Catch(Exception
e)
{
result=0;
}
但实际上,Exception是非常消耗系统性能的。除非必要,不应当使用异常控制来实现程序流程。上面的代码应当写为:
if(num!=0)
result=100/num;
else
result=0;
14、避免使用递归调用和嵌套循环,使用他们会严重影响性能,在不得不用的时候才使用。
15、禁用VB.net和Jscript动态数据类型
应当始终显示地申明变量数据类型,这能够节约程序的执行时间。以往,开发人员喜欢使用 Visual
Basic、VBScript 和 JScript
的原因之一就是它们所谓“无类型”的性质。变量不需要显式类型声明,并能够简单地通过使用来创建它们。当从一个类型到另一个类型进行分配时,转换将自动执行。不过,这种便利会大大损害应用程序的性能。
如:
为了获得最佳的性能,当声明 JScript .NET 变量时,请为其分配一个类型。例如,var A :
String;
四、使用缓存
1、使用Output Cache缓存数据
提供缓存功能是ASP.net中非常强大的一种功能。曾看到过某些评测说:ASP.net程序的性能比SUN的JSP应用程序性能快上几倍,实际上,该评测程序非常重要的一点就是使用了很多ASP.net的缓存功能。
如果你的组件是要在Asp.net应用程序中运行,你只要把System.Web.dll引用到你的项目中就可以了。然后用HttpRuntime.Cache属性就可访问Cache了(也可以通过Page.Cache或HttpContext.Cache访问)。
有以下几条缓存数据的规则。第一,数据可能会被频繁的被使用,这种数据可以缓存。第二,数据的访问频率非常高,或者一个数据的访问频率不高,但是它的生存周期很长,这样的数据最好也缓存起来。第三是一个常常被忽略的问题,有时候我们缓存了太多数据,通常在一台X86的机子上,如果你要缓存的数据超过800M的话,就会出现内存溢出的错误。所以说缓存是有限的。换名话说,你应该估计缓存集的大小,把缓存集的大小限制在10以内,否则它可能会出问题。在Asp.net中,如果缓存过大的话也会报内存溢出错误,特别是如果缓存大的DataSet对象的时候。
这里有几个你必须了解的重要的缓存机制。首先是缓存实现了“最近使用”原则( a
least-recently-used algorithm),当缓存少的时候,它会自动的强制清除那些无用的缓存。其次
“条件依赖”强制清除原则(expiration
dependencies),条件可以是时间,关键字和文件。以时间作为条件是最常用的。在asp.net2.0中增加一更强的条件,就是数据库条件。当数据库中的数据发生变化时,就会强制清除缓存。
使用
ASP.NET
缓存机制有两点需要注意。首先,不要缓存太多项。缓存每个项均有开销,特别是在内存使用方面。不要缓存容易重新计算和很少使用的项。其次,给缓存的项分配的有效期不要太短。很快到期的项会导致缓存中不必要的周转,并且经常导致更多的代码清除和垃圾回收工作。若关心此问题,请监视与
ASP.NET Applications 性能对象关联的 Cache Total Turnover Rate
性能计数器。高周转率可能说明存在问题,特别是当项在到期前被移除时。这也称作内存压力。
切记:
应该:
应该缓存那些经常被访问、同时变化频率不大的数据
应该缓存整个应用程序都要使用的设置或对象,但这些设置和对象必须在其生存期内不会变化
不应该:
不要缓存个人信息。如果缓存个人信息,其他人很容易取得这些信息。
不要缓存包含基于时间值的页面,否则浏览者将无法理解为何时间总是滞后。
不要缓存用户随时都会修改的对象,如购物车。
ASP.net中常用的缓存方式有:
1)页面缓存(对整个页面进行缓存)
<%//Response.AddHeader("Last-Modified",
DateTime.Now.AddHours(-1).ToString("r"));%>
<%
Response.AddHeader("Cache-Control", "max-age=86400");
%>
<%//Response.AddHeader("Date",
DateTime.Now.AddMinutes(-59).ToString("r"));%>
<%@Page
OutputCache VaryByParams=”classid;page” Duration=”3600”
%>
.net2.0中为:
<%@OutputCache
VaryByParam=”classid;page” Duration=”3600” %>
你就可以有效的利用第一次请求里生成的页面输出缓存内容,3600秒后重新生成一道页面内容。这种技术其实也是运用一些低层的Cache
API来实现。用页面输出缓存有几个参数可以配置,如上面所说的VaryByParams参数,该参数表示什么时候触发重输出的条件,也可以指定在 Http
Get或Http Post 请求模式下缓存输出。
例如当我们设置该参数为VaryByParams=”classid;page”的时候,default.aspx?classid=3&page=1
请求的输出都会被缓存起来。没有参数,或不用参数时用none。如果传递的参数不止一个,那么即使字符串参数与值都相同,但排列次序不同,那么在请求页面时,也将生成不同的缓存页。例如default.aspx?first=1&last=1
和default.aspx?last=1&first=1虽然参数完全相同,但由于排列次序不同,将生成两个不同的缓存页。
许多人都没有意识到当用页面输出缓存的时候,asp.net也会生成HTTP头集(HTTP
Header)保存在下游的缓存服务器中,这些信息可以用于Microsoft
Internet安全性中以及加速服务器的响应速度。当HTTP缓存的头被重置时,请求的内容会被缓在网络资源中,当客户端再次请求该内容时,就不会再从源服务器上获得内容了,而直接从缓存中获得内容。
虽然用页面输出缓存不提高你的应用程序性能,但是它能减少了从的服务器中加载已缓存页面内容的次数。当然,这仅限于缓存匿名用户可以访问的页面。因为一旦页面被缓存后,就不能再执行授权操作了。
2)片断缓存(对页面的某一部分,如某个User
Control进行缓存)
<%@
OutputCache Duration="60" VaryByParam=”TextBox1;TextBox2”
%>
在ASP.net中,除了在页面范围内使用缓存,也还可以针对User Control使用Output
Cache参数实现对用户控件的缓存。同样的,一个页面中相同类型的控件也可以有多个不同的缓存。可以根据参数来实现不同的缓存。页面缓存和片断缓存可以同时使用。
3)数据缓存
数据缓存是一种强大而又非常简单的缓存机制,它可以在缓存区中为每个应用程序保存各种对象,这些对象可以根据http的请求被调用,但是在各个不同的应用程序中这些对象都是私有的。
数据缓存是通过Cache类来实现的。当应用程序建立时,一个Cache类就同时被建立,缓存实例的生存周期就是应用程序的生存周期,它会随着应用程序的重新运行而重建,通过Cache类的方法,我们可以将数据对象放入缓存区,然后通过关键字匹配寻找并使用这些对象。
Cache类通过一个借口来控制所有需要缓存的内容,包括规定缓存的时间和缓存方式,可以通过如下方法添加缓存对象:
Cache[“关键字”]
= 关键字的取值;
然后通过下面的方法来访问这个对象:
string
mKeyValue = “”;
if(Cache[“关键字”] != null)
{
mKeyValue = Cache[“关键字”];
}
注意Page.Cache和HttpContext.Current.Cache区别:
它们指的同一个对象,在Page里,用Page.Cache,如果在global.asax或自己的类里用:HttpContext.Current.Cache,在有些事件中,由于其没有HttpContext,就用HttpRuntime.Cache。
数据缓存的过期依赖条件
某种意义上,Cache和Application是一样的,都是一种公有的对象。为了取得缓存与数据有效性之间的平衡,可以根据需要对缓存过期策略进行合理的设置。
文件依赖
Cache.Insert (“Mydata”, Source , New
CacheDependency(Server.MapPath(“authors.xml”)));
此代码的含义是当authors.xml文件不发生变化的时候,缓存MyData始终有效。
时间依赖
设定1小时后过期,这是一种绝对过期。
Cache.Insert(“Mydata”,Source,null
,DateTime.Now.AddHours(1),TimeSpan.Zero);
相对过期依赖
当DataSet不再发生变化20分钟以后,缓存过期。
Cache.Insert(“MyData”,Source,null,DateTime.MaxValue,TimeSpan.FromMinutes(20));
一个示例:
//绝对过期!!!(用来保存公用的,数据量小的数据对象,可以是任何对象)
//设置
if
(System.Web.HttpContext.Current.Cache["ok"] ==
null)
System.Web.HttpContext.Current.Cache.Insert("ok", "data", null,
DateTime.Now.AddSeconds(300),System.Web.Caching.Cache.NoSlidingExpiration);
//读取
if(System.Web.HttpContext.Current.Cache["ok"]!=null)
this.Response.Write(Convert.ToString(System.Web.HttpContext.Current.Cache.Get("ok")));
最后要注意:
在Web
Form调试期间不能使用缓存,否则你对页面所做的修改在缓存过期之前不会得到显式加载。正确的做法应该是在调试结束之后,给需要放入缓存的页面、用户控件或对象加上缓存指令。最后建立部署和安装项目,生成安装数据包,这时候就可以到服务器上去发布你的产品了。
详细参考:
http://hi.baidu.com/erics_lele/blog/item/674e46e7622f262cb83820c6.html
http://blog.csdn.net/chengking/archive/2005/10/03/494545.aspx
2、预请求缓存
虽然Cache
API设计成用来保存某段时间的数据,而预请求缓存只是保存某个时期的某个请求的内容。如果某个请求的访问频率高,而且这个请求只需要提取,应用,修改或者更新数据一次。那么就可以预缓存该请求。我们举个例子来说明。
在CS的论坛应用程序中,每一个页面的服务器控件都要求得到用于决定它的皮肤(skin)的自定义的数据,以决定用哪个样式表及其它的一些个性化的东西。这里面的某些数据可能要长时间的保存,有些时间则不然,如控件的skin数据,它只需要应用一次,而后就可以一直使用。
要实现预请求缓存,用Asp.net
的HttpContext类,HttpContext类的实例在每一个请求中创建,在请求期间的任何地方都可以通过HttpContext.Current属性访问。HttpContext类有一个Items集合属性,在请求期间所有的对象和数据都被添加到这个集合中缓存起来。和你用Cache缓存访问频率高数据一样,你可以用HttpContext.Items缓存那些每个请求都要用到的基础数据。它背后的逻辑很简单:我们向HttpContext.Items中添加一个数据,然后再从它里面读出数据。
五、配置web.config
1、一定要禁用调试模式
在部署生产应用程序或进行任何性能测量之前,始终记住禁用调试模式。如果启用了调试模式,应用程序的性能可能受到非常大的影响。
<compilation defaultLanguage="c#" debug= "false" /
>
2、必要时调整应用程序每个辅助进程的线程数
ASP.NET 的请求结构试图在执行请求的线程数和可用资源之间达到一种平衡。已知一个使用足够 CPU
功率的应用程序,该结构将根据可用于请求的 CPU
功率,来决定允许同时执行的请求数。这项技术称作线程门控。但是在某些条件下,线程门控算法不是很有效。通过使用与ASP.NET Applications
性能对象关联的 Pipeline Instance Count 性能计数器,可以在 PerfMon
中监视线程门控。
当页面调用外部资源,如数据库访问或 XML Web services
请求时,页面请求通常停止并释放 CPU。如果某个请求正在等待被处理,并且线程池中有一个线程是自由的,那么这个正在等待的请求将开始被处理。遗憾的是,有时这可能导致
Web 服务器上存在大量同时处理的请求和许多正在等待的线程,而它们对服务器性能有不利影响。通常,如果门控因子是外部资源的响应时间,则让过多请求等待资源,对
Web 服务器的吞吐量并无帮助。
为缓和这种情况,可以通过更改 Machine.config 配置文件 <processModel> 节点的 maxWorkerThreads 和
maxIOThreads 属性,手动设置进程中的线程数限制。
注意辅助线程是用来处理 ASP.NET
请求的,而 IO 线程则是用于为来自文件、数据库或 XML Web services 的数据提供服务的。
分配给这些属性的值是进程中每个 CPU
每类线程的最大数目。对于双处理器计算机,最大数是设置值的两倍。对于四处理器计算机,最大值是设置值的四倍。无论如何,对于有四个或八个 CPU
的计算机,最好更改默认值。对于有一个或两个处理器的计算机,默认值就可以,但对于有更多处理器的计算机的性能,进程中有一百或两百个线程则弊大于利。
注意进程中有太多线程往往会降低服务器的速度,因为额外的上下文交换导致操作系统将 CPU
周期花在维护线程而不是处理请求上。
<httpRuntime
maxRequestLength="4096"
useFullyQualifiedRedirectUrl="true"
minFreeThreads="8" minLocalRequestFreeThreads="4"
appRequestQueueLimit="300"
executionTimeout="45"/>
Configuration setting 默认值
推荐值
-----------------------------------
maxconnection 2 12 * #CPUs
maxIoThreads 20 100
maxWorkerThreads 20 100
minWorkerThreads 50
minFreeThreads 8 88 * #CPUs
minLocalRequestFreeThreads 4 76 * #CPUs
appRequestQueueLimit 1000
3、仔细选择会话状态提供程序
ASP.NET
为存储应用程序的会话数据提供了三种不同的方法:进程内会话状态、作为 Windows 服务的进程外会话状态和 SQL Server
数据库中的进程外会话状态。每种方法都有自己的优点,但进程内会话状态是迄今为止速度最快的解决方案。如果只在会话状态中存储少量易失数据,则建议您使用进程内提供程序。进程外解决方案主要用于跨多个处理器或多个计算机缩放应用程序,或者用于服务器或进程重新启动时不能丢失数据的情况。
<sessionState
mode= "InProc"
stateConnectionString=
"tcpip=127.0.0.1:42424"
sqlConnectionString= "data
source=127.0.0.1;Trusted_Connection=yes"
cookieless= "false"
timeout=
"20"
/ >
4、 其他
根据适当的请求和响应编码设置来配置应用程序。ASP.NET 默认编码格式为
UTF-8。如果您的应用程序为严格的 ASCII,请配置应用程序使用 ASCII
以获得稍许的性能提高。
考虑对应用程序禁用 AutoEventWireup。在 Machine.config 文件中将 AutoEventWireup 属性设置为
false,意味着页面不将方法名与事件进行匹配和将两者挂钩(例如
Page_Load)。如果页面开发人员要使用这些事件,需要在基类中重写这些方法(例如,需要为页面加载事件重写 Page.OnLoad,而不是使用
Page_Load 方法)。如果禁用
AutoEventWireup,页面将通过将事件连接留给页面作者而不是自动执行它,获得稍许的性能提升。
从请求处理管线中移除不用的模块。默认情况下,服务器计算机的 Machine.config 文件中 <httpModules>
节点的所有功能均保留为激活。根据应用程序所使用的功能,您可以从请求管线中移除不用的模块以获得稍许的性能提升。检查每个模块及其功能,并按您的需要自定义它。
例如,如果您在应用程序中不使用会话状态和输出缓存,则可以从 <httpModules>
列表中移除它们,以便请求在不执行其他有意义的处理时,不必执行每个模块的进入和离开代码。
六、其他
1、适当地使用公共语言运行库的垃圾回收器和自动内存管理
小心不要给每个请求分配过多内存,因为这样垃圾回收器将必须更频繁地进行更多的工作。另外,不要让不必要的指针指向对象,因为它们将使对象保持活动状态,并且应尽量避免含 Finalize 方法的对象,因为它们在后面会导致更多的工作。特别是在 Finalize
调用中永远不要释放资源,因为资源在被垃圾回收器回收之前可能一直消耗着内存。最后这个问题经常会对 Web 服务器环境的性能造成毁灭性的打击,因为在等待
Finalize 运行时,很容易耗尽某个特定的资源。
2、如果有大型 Web 应用程序,可考虑执行预批编译
每当发生对目录的第一次请求时都会执行批编译。如果目录中的页面没有被分析并编译,此功能会成批分析并编译目录中的所有页面,以便更好地利用磁盘和内存。如果这需要很长时间,则将快速分析并编译单个页面,以便请求能被处理。此功能带给 ASP.NET
性能上的好处,因为它将许多页面编译为单个程序集。从已加载的程序集访问一页比每页加载新的程序集要快。
批编译的缺点在于:如果服务器接收到许多对尚未编译的页面的请求,那么当 Web
服务器分析并编译它们时,性能可能较差。为解决这个问题,可以执行预批编译。为此,只需在应用程序激活之前向它请求一个页面,无论哪页均可。然后,当用户首次访问您的站点时,页面及其程序集将已被编译。
没有简单的机制可以知道批编译何时发生。需一直等到 CPU 空闲或者没有更多的编译器进程(例如
csc.exe(C# 编译器)或 vbc.exe(Visual Basic
编译器))启动。
还应尽量避免更改应用程序的 \bin 目录中的程序集。更改页面会导致重新分析和编译该页,而替换 \bin
目录中的程序集则会导致完全重新批编译该目录。
在包含许多页面的大规模站点上,更好的办法可能是根据计划替换页面或程序集的频繁程度来设计不同的目录结构。不常更改的页面可以存储在同一目录中并在特定的时间进行预批编译。经常更改的页面应在它们自己的目录中(每个目录最多几百页)以便快速编译。
Web
应用程序可以包含许多子目录。批编译发生在目录级,而不是应用程序级。
3、使请求管线内的所有模块尽可能高效
请求管线内的所有模块在每次请求中都有机会被运行。因此,当请求进入和离开模块时快速地触发代码至关重要,特别是在不使用模块功能的代码路径里。分别在使用及不使用模块和配置文件时执行吞吐量测试,对确定这些方法的执行速度非常有用。
4、避免单线程单元 (STA) COM 组件
默认情况下,ASP.NET 不允许任何 STA COM 组件在页面内运行。若要运行它们,必须在 .aspx 文件内将 ASPCompat=true
属性包含在 @ Page 指令中。这样就将执行用的线程池切换到 STA 线程池,而且使 HttpContext 和其他内置对象可用于 COM
对象。前者也是一种性能优化,因为它避免了将多线程单元 (MTA) 封送到 STA
线程的任何调用。
使用
STA COM 组件可能大大损害性能,应尽量避免。若必须使用 STA COM 组件,如在任何 interop
方案中,则应在执行期间进行大量调用并在每次调用期间发送尽可能多的信息。另外,小心不要在构造页面期间创建任何 STA COM
组件。例如下面的代码中,在页面构造时将实例化由某个线程创建的 MySTAComponent,而该线程并不是将运行页面的 STA
线程。这可能对性能有不利影响,因为要构造页面就必须完成 MTA 和 STA
线程之间的封送处理。
推荐的做法是在需要时或者在 Page_Load 方法中构造任何 COM
组件和外部资源。
永远不要将任何 STA COM 组件存储在可以由构造它的线程以外的其他线程访问的共享资源里。这类资源包括像缓存和会话状态这样的资源。即使 STA
线程调用 STA COM 组件,也只有构造此 STA COM
组件的线程能够实际为该调用服务,而这要求封送处理对创建者线程的调用。此封送处理可能产生重大的性能损失和可伸缩性问题。在这种情况下,请研究一下使 COM 组件成为
MTA COM
组件的可能性,或者更好的办法是迁移代码以使对象成为托管对象。
5、将调用密集型的 COM 组件迁移到托管代码
.NET
Framework 提供了一个简单的方法与传统的 COM
组件进行交互。其优点是可以在保留现有投资的同时利用新的平台。但是在某些情况下,保留旧组件的性能开销使得将组件迁移到托管代码是值得的。每一情况都是不一样的,决定是否需要迁移组件的最好方法是对
Web 站点运行性能测量。建议您研究一下如何将需要大量调用以进行交互的任何 COM
组件迁移到托管代码。
许多情况下不可能将旧式组件迁移到托管代码,特别是在最初迁移 Web
应用程序时。在这种情况下,最大的性能障碍之一是将数据从非托管环境封送到托管环境。因此,在交互操作中,请在任何一端执行尽可能多的任务,然后进行一个大调用而不是一系列小调用。例如,公共语言运行库中的所有字符串都是
Unicode 的,所以应在调用托管代码之前将组件中的所有字符串转换成 Unicode
格式。
另外,一处理完任何 COM
对象或本机资源就释放它们。这样,其他请求就能够使用它们,并且最大限度地减少了因稍后请求垃圾回收器释放它们所引起的性能问题。
6、对于广泛依赖外部资源的应用程序,请考虑在多处理器计算机上启用网络园艺
ASP.NET 进程模型帮助启用多处理器计算机上的可缩放性,将工作分发给多个进程(每个 CPU 一个),并且每个进程都将处理器关系设置为其
CPU。此技术称为网络园艺。如果应用程序使用较慢的数据库服务器或调用具有外部依赖项的 COM
对象(这里只是提及两种可能性),则为您的应用程序启用网络园艺是有益的。但是,在决定启用网络园艺之前,您应该测试应用程序在网络园中的执行情况。
七、性能测试
在对ASP.NET应用程序进行性能测试之前,应确保应用程序没有错误,而且功能正确。具体的性能测试可以采用以下工具进行:
1、Web Application Strees Tool (WAS)
Web
Application Strees Tool (WAS)是Microsoft发布的一个免费测试工具,可以从http://webtool.rte.microsoft.com/上下载。它可以模拟成百上千个用户同时对web应用程序进行访问请求,在服务器上形成流量负载,从而达到测试的目的,可以生成平均TTFB、平均TTLB等性能汇总报告。
2、Application Center Test (ACT)
Application Center
Test (ACT) 是一个测试工具,附带于Visual
Studio的企业版中,是Microsoft正式支持的web应用程序测试工具。它能够直观地生成图表结果,功能比WAS多,但不具备多个客户机同时测试的能力。
3、服务器操作系统"管理工具"中的"性能"计数器,可以对服务器进行监测以了解应用程序性能。
ASP.NET程序代码优化的七个方面