Asp.net程序性能优化详解
一、性能参数:
1、吞吐量
2、响应时间
3、执行时间
4、可伸缩性
二、性能因素:
1、ASPX执行环境
2、编写代码逻辑
三、提高性能的方法:
1、避免不必要的操作.例如:在Page_Load中使用IsPostBack;
2、尽量减少使用服务器端控件
3、关闭不必要的页面Session和控件的ViewState <%@Page EnableSessionState =”false”%>
4、禁用VB和JSP动态类型 <%@Page Language=”VB” Strict=”true”%>
5、使用存储过程
6、使用DateReader代替DataSet
7、关闭ASP.Net的Debug模式
8、使用ASP.Net的Output Cache缓冲
<%@ OutputCache Duration=60 VaryByParam=”None” %>
<%@ OutputCache Duration=60 VaryByParam=”TextBox1,TextBox2” %>
说明: Duration是设置Cache的过期时间;
VarByParam是设置是否根据参数而变化,None时所有参数使用同一Cache,设置TextBox1时则根据TextBox1的不同值分别缓存;当有多个参数时则要组合缓存;
9、不要使用Exception控制程序流程
try
{
result=100/num;
}
catch(Exception e)
{
result=0;
}
if(num!=0)
result = 100/num;
else
result=0;
四、缓冲分类:
1页面缓冲:根据VarByParam来进行不同的缓冲处理。
2片段缓冲:在页面控件中使用页面缓冲,当一个页面里多次使用同一个页面控件时,需要根据VarByControl来进行不同的缓冲处理。
3数据缓冲:Cache(范围是和Application一样,所有用户)
Cache.Insert(“MyData”,Source,null,new CacheDependency(Server.MapPath(“authors.xml”)));
Cache.Insert(“MyData”,Source,null,DateTime.Now.AddHours(1),TimeSpan.Zero);
Cache.Insert(“MyData”,Source,null,DateTime.MaxValue,TimeSpan.FromMinutes(20));
衡量Web性能的方法
l 衡量web服务器性能的唯一方式是对服务器进行压力测试(stress testing)
1. 自动压力测试工具是衡量的唯一方式
2. 浏览器的点击刷新并不能作为痕量手段……
l 收集多个典型应用场景方案:
1. 在应用车工女婿执行过程中模拟典型事物处理的过程
2. 痕量常用的单个页面的性能(热点)
3. 确定个场景及个页面的使用率
l 通过测试找出系统的新能指标:
1. 服务器的处理能力
2. 确定适合可接受TTFB/TTLB响应时间范围的可支持的最大客户端负载(并发用户)
性能测试工具
l 微软Web Application Stress Tool
1. 可免费下载(10MB),适用于XP、2000、2003
2. http://www.microsoft.com/technet/treeview/default.aspx?url=/technet/itsolutions/intranet/downloads/webstres.asp
l 微软应用程序中心测试工具(Microsoft Application Center Test)
1. 作为VS.NET Enterprise 的一部分提供给客户
2. 启用更丰富的脚本及报告
主要的性能测试观测项PerfMon Counters
l Processor,CPU % Utilization
Low numbers = blocking or lock contention
l ASP.NET,Requests In Application Queue
出现线型增长时表示服务器已达满负荷
l ASP.NET,Applications,Requests/Sec
动态吞吐量(应保持一致、无大的波动)
l ASP.NET,Applications,Errors Total
预示着功能级错误(应为0)
l ASP.NET App/Worker Process Restarts
表示有严重错误编程级错误(应为0)
压力测试环境的注意事项
l 在独立与Web服务器及应用服务器的机器上运行压力测试工具
1. 否则工具将超出服务器CPU的最大范围
2. 对于繁重的负载使用多个客户端机器
提高Asp.Net应用程序的十大方法
译完了提高Asp.Net应用程序的十大方法这篇文章,仔细想其中提到的每一条,在这里结合我的项目来谈谈.
第一条:返回多个结果集
因为我的项目中所有对数据库的访问的sql语句都是通过调用存储过程实现的,所以基本上都是用一个存储过程完成返回多个结果集,来得到自己想要的数据.满足!!
第二条:对数据进行分页
我写了一个通用的分页存储过程,用于对显示的数据进行分页,参照了原来Dino Esposito 的分页思想写的,写成了一篇项目总结发表在CSDN上.经过几个项目后,发现分页的效率取决于用于分页的条件,一般情况下,用索引字段的来作条件分页的效率是最高的,所以在以后的项目中,要注意分页的效率.查了一下csdn中的分页存储过程,"风云"的那个分页存储过程比较通用,而且效率也高.决定以后用它的那个了.
网址:http://community.csdn.net/Expert/topic/3587/3587201.xml?temp=.6331598
CREATE PROCEDURE sp_page
@tb
varchar(50), --表名
@col
varchar(50), --按该列来进行分页
@coltype
int,
--@col列的类型,0-数字类型,1-字符类型,2-日期时间类型
@orderby
bit,
--排序,0-顺序,1-倒序
@collist
varchar(800),--要查询出的字段列表,*表示全部字段
@pagesize int,
--每页记录数
@page
int,
--指定页
@condition varchar(800),--查询条件
@pages
int OUTPUT --总页数
AS
/*
功能描述:对指定表中满足条件的记录按指定列进行分页查询,分页可以顺序、倒序
查询可以指定页大小、指定查询任意页、指定输出字段列表,返回总页数
*/
DECLARE @sql nvarchar(4000),@where1 varchar(800),@where2 varchar(800)
IF @condition is null or rtrim(@condition)=''
BEGIN--没有查询条件
SET @where1=' WHERE '
SET @where2=' '
END
ELSE
BEGIN--有查询条件
SET @where1=' WHERE ('+@condition+') AND '--本来有条件再加上此条件
SET @where2=' WHERE ('+@condition+') '--原本没有条件而加上此条件
END
SET @sql='SELECT @pages=CEILING((COUNT(*)+0.0)/'+CAST(@pagesize AS varchar)+
') FROM '+@tb+@where2
EXEC sp_executesql @sql,N'@pages int OUTPUT',@pages OUTPUT--计算总页数
IF @orderby=0
SET @sql='SELECT TOP '+CAST(@pagesize AS varchar)+' '+@collist+
' FROM (SELECT">'+@tb+@where1+@col+'>(SELECT MAX('+@col+') '+
' FROM (SELECT TOP '+CAST(@pagesize*(@page-1) AS varchar)+' '+
@col+' FROM '+@tb+@where2+'ORDER BY '+@col+') t) ORDER BY '+@col
ELSE
SET @sql='SELECT TOP '+CAST(@pagesize AS varchar)+' '+@collist+
' FROM '+@tb+@where1+@col+'<(SELECT MIN('+@col+') '+
' FROM (SELECT TOP '+CAST(@pagesize*(@page-1) AS varchar)+' '+
@col+' FROM '+@tb+@where2+'ORDER BY '+@col+' DESC) t) ORDER BY '+
@col+' DESC'
IF @page=1--第一页
SET @sql='SELECT TOP '+CAST(@pagesize AS varchar)+' '+@collist+' FROM '+@tb+
@where2+'ORDER BY '+@col+CASE @orderby WHEN 0 THEN '' ELSE ' DESC' END
EXEC(@sql)
GO
第三条:连接池
默认情况下,.NET是使用连接池来管理连接的.所以在项目上注意两点:一个是以构造一个类,专门用于返回连接字符串或连接对象,这样要以保证连接字符串是相同的,才能有效的利用连接池.另一个是用完连接后马上关闭边接.
第四,五,七条:充分利用Asp.Net中的各种缓存技术(文章的第四,第五,第七个方法都是用了缓存)
原来的项目中,用过一次页面输出缓存,但是没有成功,因为页面要提交,所以不能及时响应事件.就把它下掉了.开始以为是用缓存的问题.通过这篇文章,才知道是自己不知道怎么用.所以这段时间就在看有关怎么利用缓存的资料.应该好好的了解缓存的用法.这样,才能更有效的提高应用程序的效率.如果你也想了解缓存的技术,我找到了以下的几篇文章,它们都在Msdn中文网上:
ASP.NET 缓存:方法和最佳实践:
http://www.microsoft.com/china/MSDN/library/WebServices/ASP.NET/ASP.NETCaching-TechniquesandBestPractiCEs.mspx
在 ASP.NET 中支持数据库缓存相关性:
http://www.microsoft.com/china/msdn/library/webservices/asp.net/DbCacheDepASPNET.mspx
使用缓存,节省资金:
http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspnetasp11022004.mspx
为 ASP.NET 创建缓存配置对象:
http://www.microsoft.com/china/MSDN/library/WebServices/ASP.NET/CreatingaCacheConfigurationObjectforASPNET.mspx
还有一本微软的蓝皮书:
Improving .NET Application Performance and Scalability
应该说这一本书是最全,最好的指南了.第六章有一节专门讲缓存及缓存应用指南的.正在看这一章.
第六条:在后台处理
这是我接触到的一个新的解决方案,原来asp.net中也可以作后台的定时触发功能.以后肯定能用上.上面列出的
使用缓存,节省资金:
http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspnetasp11022004.mspx
这篇文章就使用了这一种技术,很有参考价值.
第八,九条:用IIS6的新功能
原来一直没有注意IIS6,都是用IIS5,看来服务器应该升级了.
第十条:有条件的使用ViewState
一直都没注意ViewState,看来以后只要没有必须使用ViewState的时候就要把它关掉,特别是用到了DataGrid的页面.
总的看来,其中用Cache能更好的提高应用程序的性能,如果用到数据库,记得要充分利用连接池,高效率的分页;如果用的是windows 2003就不要忘了开它的Kernel Caching.最后也不要忘了关不用ViewState的页面或控件的EnableViewState属性.
优化经典:
一、SqlDataRead和Dataset的选择
Sqldataread优点:读取数据非常快。如果对返回的数据不需做大量处理的情况下,建议使用SqlDataReader,其性能要比datset好很多。缺点:直到数据读完才可close掉于数据库的连接
(SqlDataReader 读数据是快速向前的。SqlDataReader 类提供了一种读取从 SQL Server 数据库检索的只进数据流的方法。它使用 SQL Server 的本机网络数据传输格式从数据库连接直接读取数据。DataReader需及时显式的close。可及时的释放对数据的连接。)
Dataset是把数据读出,缓存在内存中。缺点:对内存的占用较高。如果对返回的数据需做大量的处理用Dataset比较好些可以减少对数据库的连接操作。优点:只需连接一次就可close于数据库的连接
*一般情况下,读取大量数据,对返回数据不做大量处理用SqlDataReader.对返回数据大量处理用datset比较合适.对SqlDataReader和Dataset的选择取决于程序功能的实现。
二、ExecuteNonQuery和ExecuteScalar
对数据的更新不需要返回结果集,建议使用ExecuteNonQuery。由于不返回结果集可省掉网络数据传输。它仅仅返回受影响的行数。如果只需更新数据用ExecuteNonQuery性能的开销比较小。
ExecuteScalar它只返回结果集中第一行的第一列。使用 ExecuteScalar 方法从数据库中检索单个值(例如id号)。与使用 ExecuteReader 方法,返回的数据执行生成单个值所需的操作相比,此操作需要的代码较少。
*只需更新数据用ExecuteNonQuery.单个值的查询使用ExecuteScalar数据绑定的选择
三、数据的绑定DataBinder
一般的绑定方法<%# DataBinder.Eval(Container.DataItem, "字段名") %>用DataBinder.eval 绑定不必关心数据来源(Dataread或dataset)。不必关心数据的类型eval会把这个数据对象转换为一个字符串。在底层绑定做了很多工作,使用了反射性能。正因为使用方便了,但却影响了数据性能。来看下<%# DataBinder.Eval(Container.DataItem, "字段名") %>。当于dataset绑定时,DataItem其实式一个DataRowView(如果绑定的是一个数据读取器(dataread)它就是一个IdataRecord。)因此直接转换成DataRowView的话,将会给性能带来很大提升。
<%# ctype(Container.DataItem,DataRowView).Row("字段名") %>
*对数据的绑定建议使用<%# ctype(Container.DataItem,DataRowView).Row("字段名") %>。数据量大的时候可提高几百倍的速度。使用时注意2方面:1.需在页面添加<%@ Import namespace="System.Data"%>.2.注意字段名的大小写(要特别注意)。如果和查询的不一致,在某些情况下会导致比<%# DataBinder.Eval(Container.DataItem, "字段名") %>还要慢。如果想进一步提高速度,可采用<%# ctype(Container.DataItem,DataRowView).Row(0) %>的方法。不过其可读性不高。
以上的是vb.net的写法。在c#中:<@% ((DataRowView)Container.DataItem)["字段名"] %>
对查看页面每个执行过程状态最简单的办法:其页面的trace属性为true就可查看细节。
一、使用存储过程:
1、性能方面:存储过程提供了许多标准sql语言中所没有的高级特性。其传递参数和执行逻辑表达式的功能,有助于应用程序设计者处理复杂任务。另外,存储过程存储在本地服务器上,减少了执行该过程所需的网络传输宽带和执行时间。(存储过程已经对sql语句进行了预编译,所以其执行速度比在程序里执行sql语句快很多)
2、程序结构方面:从程序的可扩展性看,使用存储过程会对程序以后的修改带来方便。比如数据库的结构改变了,只需修改相对应的存储结构,和程序中的调用部分即可。这部分不属于本文探讨范围,属于程序结构设计方面。所以不在此展开。
3、程序安全性:使用存储过程可避免SQL Injection攻击。
二、查询语句的优化(针对sql server2000)
很多人只为目的写出sql语句,而不考虑sql语句的执行效率。在这我只提供一优化表顺序的方法,(sql语句的优化和原则将会在我的sql server2000学习笔记中专题讨论)
对sql语句执行效率可用sql server2000的查询分析器来查看语句的执行过程。
优化表顺序:一般情况下,sqlserver 会对表的连接作出自动优化。例如:select name,no from A join B on A. id=B.id join C on C.id=A.id where name=’wang’
尽管A表在From中先列出,然后才是B,最后才是C。但sql server可能会首先使用c表。它的选择原则是相对于该查询限制为单行或少数几行,就可以减少在其他表中查找的总数据量。绝大多数情况下,sql server 会作出最优的选择,但如果你发觉某个复杂的联结查询速度比预计的要慢,就可以使用SET FORCEPLAN语句强制sql server按照表出现顺序使用表。如上例加上:SET FORCEPLAN ON…….SET FORCEPLAN OFF 表的执行顺序将会按照你所写的顺序执行。在查询分析器中查看2种执行效率,从而选择表的连接顺序。
*使用SET FORCEPLAN选择表联结顺序
三、页面的优化(.aspx)
主要针对几个页面属性
1、EnableViewState(页面的视图状态)。如果无特殊要求设置为false。使用ViewState ,每个对象都必须先序列化到 ViewState 中,然后再通过回传进行反序列化,因此使用 ViewState是没有代价的。尽量减少使用对象,如果可能,尽量减少放入 ViewState 中的对象的数目。下面情况基本上可以禁用viewstate:
(1)页面控件(.ascx)
(2)页面不回传给自身。
(3)无需对控件的事件处理。
(4)控件没有动态的或数据绑定的属性值(或对于每个postpack都在代码中处理)
单个页面或每个页面都禁用 ViewState,如下所示:单个页面:<%@ Page EnableViewState="False" %> 每个页面:在 web.config 中 <Pages EnableViewState="false" /> EnableSessionState保持默认值即可(如果页面用到sessionstate它才会占用资源)。EnableViewStateMac如果无安全上的特殊要求,保持默认值。
2、Pagelayout.页面布局模型。建议使用Flowlayout(元素不带绝对定位属性添加).Gridlayout(绝对定位属性)由于采用绝对定位,将会比Flowlayout生产更多的代码,主要是控件的定位信息。
3、项目发布的时候切记解除页面的Debug状态。
4、Html语言的优化。我的建议是熟练掌握Html/JavaScript,少用vs.net2003自动生产的代码,它会自动生成一些无用的html代码。
5、smart navigation设置为true能让用户明显的感觉性能提高。启用此属性后对客户端和服务端影响不大.它能智能涮新需要涮新需涮新的部分.
四、控件的选择:
Html控件和服务器控件的选择。服务器控件带来的方便和功能上的实现是html控件所不能比拟的。但是是以牺牲服务器端的资源来取得的。我个人建议:如果html控件达不到所要实现的功能,而且和一些脚本语言(如javascrpt/vbscript)结合也不能实现的话。才会选择服务器控件。选择服务器控件后,也尽量对其控件优化,如取消一些页面状态等(具体看控件的优化)
服务器控件的选择:主要针对几个常用数据控件说明一下:
DataGrid:自带最强大的数据显示控件,内置了对数据的修改、删除、添加、分页等很多实用功能。如果你只需对数据显示的话,尽量不要选择DataGrid(它把数据都存储在viewstate中).也不要使用自带的分页功能,microsoft在自动分页的底层做了很多工作,虽然使用方便了,但性能开销大了。
DataList:比DataGrid功能少了很多。但自定义性强了很多。特有的多行数据显示,给我们带来了很多方便。DataGrid能实现的功能,它基本能实现。所以建议使用它。
Repeater:功能最少,但自定义性非常强。如果只需对数据显示,建议使用。由于减少了很多功能,对服务器的性能带来消耗最小。因此,如果是对数据显示的话,我基本上都是选择Repeater然后DataList最后DataGrid
*尽量选择html控件。能在客户端实现的功能就在客户端实现(熟练掌握javascript),减少服务器的压力。数据控件选择顺序:Repeater、DataList、DataGrid
五、服务器控件的优化:
1、Viewstate
控件的viewstate与页面的viewstate基本是一致的。用来保存控件的一些状态。处理原则和处理页面的viewstate一样。有兴趣的可以用Datagrid绑定数据测试下viewstate保存的数据量有多大,它所保存的数据基本和Datagrid显示的数据量大小是等同的。
2、Ispostpack
默认false.需要产生事件的时候才需设置为true.
控件的优化,主要看你对此控件的熟悉情况。对控件内部运作的原理越了解,就会对其作出合适的优化。
性能优化是三两句话说不清的,我所写出的仅仅是冰山一角,性能的优化是靠平时经验的积累和对程序的运作原理的不断认知。
ASP.NET中常用的优化性能方法
1. 数据库访问性能优化
数据库的连接和关闭
访问数据库资源需要创建连接、打开连接和关闭连接几个操作。这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源。ASP.NET中提供了连接池(Connection Pool)改善打开和关闭数据库对性能的影响。系统将用户的数据库连接放在连接池中,需要时取出,关闭时收回连接,等待下一次的连接请求。
连接池的大小是有限的,如果在连接池达到最大限度后仍要求创建连接,必然大大影响性能。因此,在建立数据库连接后只有在真正需要操作时才打开连接,使用完毕后马上关闭,从而尽量减少数据库连接打开的时间,避免出现超出连接限制的情况。
使用存储过程
存储过程是存储在服务器上的一组预编译的SQL语句,类似于DOS系统中的批处理文件。存储过程具有对数据库立即访问的功能,信息处理极为迅速。使用存储过程可以避免对命令的多次编译,在执行一次后其执行规划就驻留在高速缓存中,以后需要时只需直接调用缓存中的二进制代码即可。
另外,存储过程在服务器端运行,独立于ASP.NET程序,便于修改,最重要的是它可以减少数据库操作语句在网络中的传输。
优化查询语句
ASP.NET中ADO连接消耗的资源相当大,SQL语句运行的时间越长,占用系统资源的时间也越长。因此,尽量使用优化过的SQL语句以减少执行时间。比如,不在查询语句中包含子查询语句,充分利用索引等。
2. 字符串操作性能优化
使用值类型的ToString方法
在连接字符串时,经常使用"+"号直接将数字添加到字符串中。这种方法虽然简单,也可以得到正确结果,但是由于涉及到不同的数据类型,数字需要通过装箱操作转化为引用类型才可以添加到字符串中。但是装箱操作对性能影响较大,因为在进行这类处理时,将在托管堆中分配一个新的对象,原有的值复制到新创建的对象中。
使用值类型的ToString方法可以避免装箱操作,从而提高应用程序性能。
运用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. 优化 Web 服务器计算机和特定应用程序的配置文件以符合您的特定需要
默认情况下,ASP.NET 配置被设置成启用最广泛的功能并尽量适应最常见的方案。因此,应用程序开发人员可以根据应用程序所使用的功能,优化和更改其中的某些配置,以提高应用程序的性能。下面的列表是您应该考虑的一些选项。
仅对需要的应用程序启用身份验证。默认情况下,身份验证模式为 Windows,或集成 NTLM。大多数情况下,对于需要身份验证的应用程序,最好在 Machine.config 文件中禁用身份验证,并在 Web.config 文件中启用身份验证。
根据适当的请求和响应编码设置来配置应用程序。ASP.NET 默认编码格式为 UTF-8。如果您的应用程序为严格的 ASCII,请配置应用程序使用 ASCII 以获得稍许的性能提高。
考虑对应用程序禁用 AutoEventWireup。在 Machine.config 文件中将 AutoEventWireup 属性设置为 false,意味着页面不将方法名与事件进行匹配和将两者挂钩(例如 Page_Load)。如果页面开发人员要使用这些事件,需要在基类中重写这些方法(例如,需要为页面加载事件重写 Page.OnLoad,而不是使用 Page_Load 方法)。如果禁用 AutoEventWireup,页面将通过将事件连接留给页面作者而不是自动执行它,获得稍许的性能提升。
从请求处理管线中移除不用的模块。默认情况下,服务器计算机的 Machine.config 文件中 <httpModules> 节点的所有功能均保留为激活。根据应用程序所使用的功能,您可以从请求管线中移除不用的模块以获得稍许的性能提升。检查每个模块及其功能,并按您的需要自定义它。
例如,如果您在应用程序中不使用会话状态和输出缓存,则可以从 <httpModules>列表中移除它们,以便请求在不执行其他有意义的处理时,不必执行每个模块的进入和离开代码。
4. 一定要禁用调试模式
在部署生产应用程序或进行任何性能测量之前,始终记住禁用调试模式。如果启用了调试模式,应用程序的性能可能受到非常大的影响。
5. 对于广泛依赖外部资源的应用程序,请考虑在多处理器计算机上启用网络园艺
ASP.NET 进程模型帮助启用多处理器计算机上的可缩放性,将工作分发给多个进程(每个 CPU 一个),并且每个进程都将处理器关系设置为其 CPU。此技术称为网络园艺。如果应用程序使用较慢的数据库服务器或调用具有外部依赖项的 COM 对象(这里只是提及两种可能性),则为您的应用程序启用网络园艺是有益的。但是,在决定启用网络园艺之前,您应该测试应用程序在网络园中的执行情况。
6. 只要可能,就缓存数据和页输出
ASP.NET 提供了一些简单的机制,它们会在不需要为每个页请求动态计算页输出或数据时缓存这些页输出或数据。另外,通过设计要进行缓存的页和数据请求(特别是在站点中预期将有较大通讯量的区域),可以优化这些页的性能。与 .NET Framework 的任何 Web 窗体功能相比,适当地使用缓存可以更好的提高站点的性能,有时这种提高是超数量级的。
使用 ASP.NET 缓存机制有两点需要注意。首先,不要缓存太多项。缓存每个项均有开销,特别是在内存使用方面。不要缓存容易重新计算和很少使用的项。其次,给缓存的项分配的有效期不要太短。很快到期的项会导致缓存中不必要的周转,并且经常导致更多的代码清除和垃圾回收工作。若关心此问题,请监视与 ASP.NET Applications 性能对象关联的 Cache Total Turnover Rate 性能计数器。高周转率可能说明存在问题,特别是当项在到期前被移除时。这也称作内存压力。
7. 选择适合页面或应用程序的数据查看机制
根据您选择在 Web 窗体页显示数据的方式,在便利和性能之间常常存在着重要的权衡。例如,DataGrid Web 服务器控件可能是一种显示数据的方便快捷的方法,但就性能而言它的开销常常是最大的。在某些简单的情况下,您通过生成适当的 HTML 自己呈现数据可能很有效,但是自定义和浏览器定向会很快抵销所获得的额外功效。Repeater Web 服务器控件是便利和性能的折衷。它高效、可自定义且可编程。
8. 将 SqlDataReader 类用于快速只进数据游标
SqlDataReader 类提供了一种读取从 SQL Server 数据库检索的只进数据流的方法。如果当创建 ASP.NET 应用程序时出现允许您使用它的情况,则 SqlDataReader 类提供比 DataSet 类更高的性能。情况之所以这样,是因为 SqlDataReader 使用 SQL Server 的本机网络数据传输格式从数据库连接直接读取数据。另外,SqlDataReader 类实现 IEnumerable 接口,该接口也允许您将数据绑定到服务器控件。有关更多信息,请参见 SqlDataReader 类。有关 ASP.NET 如何访问数据的信息,请参见通过 ASP.NET 访问数据。
9. 将 SQL Server 存储过程用于数据访问
在 .NET Framework 提供的所有数据访问方法中,基于 SQL Server 的数据访问是生成高性能、可缩放 Web 应用程序的推荐选择。使用托管 SQL Server 提供程序时,可通过使用编译的存储过程而不是特殊查询获得额外的性能提高。
10. 避免单线程单元 (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 Language="VB" ASPCompat="true" %>
<script runat=server>
Dim myComp as new MySTAComponent()
Public Sub Page_Load()
myComp.Name = "Bob"
End Sub
</script>
<html>
<%
Response.Write(myComp.SayHello)
%>
</html>
首选机制是推迟对象的创建,直到以后在 STA 线程下执行上述代码,如下面的例子所示。
<%@ Page Language="VB" ASPCompat="true" %>
<script runat=server>
Dim myComp
Public Sub Page_Load()
myComp = new MySTAComponent()
myComp.Name = "Bob"
End Sub
</script>
<html>
<%
Response.Write(myComp.SayHello)
%>
</html>
推荐的做法是在需要时或者在 Page_Load 方法中构造任何 COM 组件和外部资源。
永远不要将任何 STA COM 组件存储在可以由构造它的线程以外的其他线程访问的共享资源里。这类资源包括像缓存和会话状态这样的资源。即使 STA 线程调用 STA COM 组件,也只有构造此 STA COM 组件的线程能够实际为该调用服务,而这要求封送处理对创建者线程的调用。此封送处理可能产生重大的性能损失和可伸缩性问题。在这种情况下,请研究一下使 COM 组件成为 MTA COM 组件的可能性,或者更好的办法是迁移代码以使对象成为托管对象。
11. 将调用密集型的 COM 组件迁移到托管代码
.NET Framework 提供了一个简单的方法与传统的 COM 组件进行交互。其优点是可以在保留现有投资的同时利用新的平台。但是在某些情况下,保留旧组件的性能开销使得将组件迁移到托管代码是值得的。每一情况都是不一样的,决定是否需要迁移组件的最好方法是对 Web 站点运行性能测量。建议您研究一下如何将需要大量调用以进行交互的任何 COM 组件迁移到托管代码。
许多情况下不可能将旧式组件迁移到托管代码,特别是在最初迁移 Web 应用程序时。在这种情况下,最大的性能障碍之一是将数据从非托管环境封送到托管环境。因此,在交互操作中,请在任何一端执行尽可能多的任务,然后进行一个大调用而不是一系列小调用。例如,公共语言运行库中的所有字符串都是 Unicode 的,所以应在调用托管代码之前将组件中的所有字符串转换成 Unicode 格式。
另外,一处理完任何 COM 对象或本机资源就释放它们。这样,其他请求就能够使用它们,并且最大限度地减少了因稍后请求垃圾回收器释放它们所引起的性能问题。
12. 在 Visual Basic .NET 或 JScript 代码中使用早期绑定
以往,开发人员喜欢使用 Visual Basic、VBScript 和 JScript 的原因之一就是它们所谓“无类型”的性质。变量不需要显式类型声明,并能够简单地通过使用来创建它们。当从一个类型到另一个类型进行分配时,转换将自动执行。不过,这种便利会大大损害应用程序的性能。
Visual Basic 现在通过使用 Option Strict 编译器指令来支持类型安全编程。为了向后兼容,默认情况下,ASP.NET 不启用该选项。但是,为了得到最佳性能,强烈建议在页中启用该选项。若要启用 Option Strict,请将 Strict 属性包括在 @ Page 指令中,或者,对于用户控件,请将该属性包括在 @ Control 指令中。下面的示例演示了如何设置该属性,并进行了四个变量调用以显示使用该属性是如何导致编译器错误的。
<%@ Page Language="VB" Strict="true" %>
<%
Dim B
Dim C As String
' This will cause a compiler error.
A = "Hello"
' This will cause a compiler error.
B = "World"
' This will not cause a compiler error.
C = "!!!!!!"
' But this will cause a compiler error.
C = 0
%>
JScript .NET 也支持无类型编程,但它不提供强制早期绑定的编译器指令。若发生下面任何一种情况,则变量是晚期绑定的:
被显式声明为 Object。
是无类型声明的类的字段。
是无显式类型声明的专用函数或方法成员,并且无法从其使用推断出类型。
最后一个差别比较复杂,因为如果 JScript .NET 编译器可以根据变量的使用情况推断出类型,它就会进行优化。在下面的示例中,变量 A 是早期绑定的,但变量 B 是晚期绑定的。
var A;
var B;
A = "Hello";
B = "World";
B = 0;
为了获得最佳的性能,当声明 JScript .NET 变量时,请为其分配一个类型。例如,var A : String。
13. 使请求管线内的所有模块尽可能高效
请求管线内的所有模块在每次请求中都有机会被运行。因此,当请求进入和离开模块时快速地触发代码至关重要,特别是在不使用模块功能的代码路径里。分别在使用及不使用模块和配置文件时执行吞吐量测试,对确定这些方法的执行速度非常有用。
14. 使用 HttpServerUtility.Transfer 方法在同一应用程序的页面间重定向
采用 Server.Transfer 语法,在页面中使用该方法可避免不必要的客户端重定向。
15. 必要时调整应用程序每个辅助进程的线程数
ASP.NET 的请求结构试图在执行请求的线程数和可用资源之间达到一种平衡。已知一个使用足够 CPU 功率的应用程序,该结构将根据可用于请求的 CPU 功率,来决定允许同时执行的请求数。这项技术称作线程门控。但是在某些条件下,线程门控算法不是很有效。通过使用与 ASP.NET Applications 性能对象关联的 Pipeline Instance Count 性能计数器,可以在 PerfMon 中监视线程门控。
当页面调用外部资源,如数据库访问或 XML Web services 请求时,页面请求通常停止并释放 CPU。如果某个请求正在等待被处理,并且线程池中有一个线程是自由的,那么这个正在等待的请求将开始被处理。遗憾的是,有时这可能导致 Web 服务器上存在大量同时处理的请求和许多正在等待的线程,而它们对服务器性能有不利影响。通常,如果门控因子是外部资源的响应时间,则让过多请求等待资源,对 Web 服务器的吞吐量并无帮助。
为缓和这种情况,可以通过更改 Machine.config 配置文件节点的 maxWorkerThreads 和 maxIOThreads 属性,手动设置进程中的线程数限制。
注意辅助线程是用来处理 ASP.NET 请求的,而 IO 线程则是用于为来自文件、数据库或 XML Web services 的数据提供服务的。
分配给这些属性的值是进程中每个 CPU 每类线程的最大数目。对于双处理器计算机,最大数是设置值的两倍。对于四处理器计算机,最大值是设置值的四倍。无论如何,对于有四个或八个 CPU 的计算机,最好更改默认值。对于有一个或两个处理器的计算机,默认值就可以,但对于有更多处理器的计算机的性能,进程中有一百或两百个线程则弊大于利。
注意进程中有太多线程往往会降低服务器的速度,因为额外的上下文交换导致操作系统将 CPU 周期花在维护线程而不是处理请求上。
16. 适当地使用公共语言运行库的垃圾回收器和自动内存管理
小心不要给每个请求分配过多内存,因为这样垃圾回收器将必须更频繁地进行更多的工作。另外,不要让不必要的指针指向对象,因为它们将使对象保持活动状态,并且应尽量避免含 Finalize 方法的对象,因为它们在后面会导致更多的工作。特别是在 Finalize 调用中永远不要释放资源,因为资源在被垃圾回收器回收之前可能一直消耗着内存。最后这个问题经常会对 Web 服务器环境的性能造成毁灭性的打击,因为在等待 Finalize 运行时,很容易耗尽某个特定的资源。
17. 如果有大型 Web 应用程序,可考虑执行预批编译
每当发生对目录的第一次请求时都会执行批编译。如果目录中的页面没有被分析并编译,此功能会成批分析并编译目录中的所有页面,以便更好地利用磁盘和内存。如果这需要很长时间,则将快速分析并编译单个页面,以便请求能被处理。此功能带给 ASP.NET 性能上的好处,因为它将许多页面编译为单个程序集。从已加载的程序集访问一页比每页加载新的程序集要快。
批编译的缺点在于:如果服务器接收到许多对尚未编译的页面的请求,那么当 Web 服务器分析并编译它们时,性能可能较差。为解决这个问题,可以执行预批编译。为此,只需在应用程序激活之前向它请求一个页面,无论哪页均可。然后,当用户首次访问您的站点时,页面及其程序集将已被编译。
没有简单的机制可以知道批编译何时发生。需一直等到 CPU 空闲或者没有更多的编译器进程(例如 csc.exe(C# 编译器)或 vbc.exe(Visual Basic 编译器))启动。
还应尽量避免更改应用程序的 /bin 目录中的程序集。更改页面会导致重新分析和编译该页,而替换 /bin 目录中的程序集则会导致完全重新批编译该目录。
在包含许多页面的大规模站点上,更好的办法可能是根据计划替换页面或程序集的频繁程度来设计不同的目录结构。不常更改的页面可以存储在同一目录中并在特定的时间进行预批编译。经常更改的页面应在它们自己的目录中(每个目录最多几百页)以便快速编译。
Web 应用程序可以包含许多子目录。批编译发生在目录级,而不是应用程序级。
18. 不要依赖代码中的异常
因为异常大大地降低性能,所以您不应该将它们用作控制正常程序流程的方式。如果有可能检测到代码中可能导致异常的状态,请执行这种操作。不要在处理该状态之前捕获异常本身。常见的方案包括:检查 null,分配给将分析为数字值的 String 一个值,或在应用数学运算前检查特定值。下面的示例演示可能导致异常的代码以及测试是否存在某种状态的代码。两者产生相同的结果。
try
{
result = 100 / num;
}
catch (Exception e)
{
result = 0;
}
// ...to this.
if (num != 0)
result = 100 / num;
else
result = 0;
19. 使用 HttpResponse.Write 方法进行字符串串联
该方法提供非常有效的缓冲和连接服务。但是,如果您正在执行广泛的连接,请使用多个 Response.Write 调用。下面示例中显示的技术比用对 Response.Write 方法的单个调用连接字符串更快。
Response.Write("a");
Response.Write(myString);
Response.Write("b");
Response.Write(myObj.ToString());
Response.Write("c");
Response.Write(myString2);
Response.Write("d");
20. 除非有特殊的原因要关闭缓冲,否则使其保持打开
禁用 Web 窗体页的缓冲会导致大量的性能开销。
21. 只在必要时保存服务器控件视图状态
自动视图状态管理是服务器控件的功能,该功能使服务器控件可以在往返过程上重新填充它们的属性值(您不需要编写任何代码)。但是,因为服务器控件的视图状态在隐藏的窗体字段中往返于服务器,所以该功能确实会对性能产生影响。您应该知道在哪些情况下视图状态会有所帮助,在哪些情况下它影响页的性能。例如,如果您将服务器控件绑定到每个往返过程上的数据,则将用从数据绑定操作获得的新值替换保存的视图状态。在这种情况下,禁用视图状态可以节省处理时间。
默认情况下,为所有服务器控件启用视图状态。若要禁用视图状态,请将控件的EnableViewState 属性设置为 false,如下面的 DataGrid 服务器控件示例所示。
<asp:datagrid EnableViewState="false" datasource="..."
runat="server"/>
您还可以使用 @ Page 指令禁用整个页的视图状态。当您不从页回发到服务器时,这将十分有用:
<%@ Page EnableViewState="false" %>
注意 @ Control 指令中也支持 EnableViewState 属性,该指令允许您控制是否为用户控件启用视图状态。
若要分析页上服务器控件使用的视图状态的数量,请(通过将 trace="true" 属性包括在 @ Page 指令中)启用该页的跟踪并查看 Control Hierarchy 表的 Viewstate 列。有关跟踪和如何启用它的信息,请参见 ASP.NET 跟踪。
22. 避免到服务器的不必要的往返过程
虽然您很可能希望尽量多地使用 Web 窗体页框架的那些节省时间和代码的功能,但在某些情况下却不宜使用 ASP.NET 服务器控件和回发事件处理。
通常,只有在检索或存储数据时,您才需要启动到服务器的往返过程。多数数据操作可在这些往返过程间的客户端上进行。例如,从 HTML 窗体验证用户输入经常可在数据提交到服务器之前在客户端进行。通常,如果不需要将信息传递到服务器以将其存储在数据库中,那么您不应该编写导致往返过程的代码。
如果您开发自定义服务器控件,请考虑让它们为支持 ECMAScript 的浏览器呈现客户端代码。通过以这种方式使用服务器控件,您可以显著地减少信息被不必要的发送到 Web 服务器的次数。
使用 Page.IsPostBack 避免对往返过程执行不必要的处理
如果您编写处理服务器控件回发处理的代码,有时可能需要在首次请求页时执行其他代码,而不是当用户发送包含在该页中的 HTML 窗体时执行的代码。根据该页是否是响应服务器控件事件生成的,使用 Page.IsPostBack 属性有条件地执行代码。例如,下面的代码演示如何创建数据库连接和命令,该命令在首次请求该页时将数据绑定到 DataGrid 服务器控件。
void Page_Load(Object sender, EventArgs e)
{
// Set up a connection and command here.
if (!Page.IsPostBack)
{
String query = "select * from Authors where FirstName like '%JUSTIN%'";
myCommand.Fill(ds, "Authors");
myDataGrid.DataBind();
}
}
由于每次请求时都执行 Page_Load 事件,上述代码检查 IsPostBack 属性是否设置为 false。如果是,则执行代码。如果该属性设置为 true,则不执行代码。
注意如果不运行这种检查,回发页的行为将不更改。Page_Load 事件的代码在执行服务器控件事件之前执行,但只有服务器控件事件的结果才可能在输出页上呈现。如果不运行该检查,仍将为 Page_Load 事件和该页上的任何服务器控件事件执行处理。
23. 当不使用会话状态时禁用它
并不是所有的应用程序或页都需要针对于具体用户的会话状态,您应该对任何不需要会话状态的应用程序或页禁用会话状态。
若要禁用页的会话状态,请将 @ Page 指令中的 EnableSessionState 属性设置为 false。例如:
<%@ Page EnableSessionState="false" %>
注意如果页需要访问会话变量,但不打算创建或修改它们,则将 @ Page 指令中的 EnableSessionState 属性设置为 ReadOnly。
还可以禁用 XML Web services 方法的会话状态。有关更多信息,请参见使用 ASP.NET 和 XML Web services 客户端创建的 XML Web services。
若要禁用应用程序的会话状态,请在应用程序 Web.config 文件的 sessionstate 配置节中将 mode 属性设置为 off。例如:
<sessionstate mode="off" />
24. 仔细选择会话状态提供程序
ASP.NET 为存储应用程序的会话数据提供了三种不同的方法:进程内会话状态、作为 Windows 服务的进程外会话状态和 SQL Server 数据库中的进程外会话状态。每种方法都有自己的优点,但进程内会话状态是迄今为止速度最快的解决方案。如果只在会话状态中存储少量易失数据,则建议您使用进程内提供程序。进程外解决方案主要用于跨多个处理器或多个计算机缩放应用程序,或者用于服务器或进程重新启动时不能丢失数据的情况。有关更多信息,请参见 ASP.NET 状态管理。
25. 不使用不必要的Server Control
ASP.net中,大量的服务器端控件方便了程序开发,但也可能带来性能的损失,因为用户每操作一次服务器端控件,就产生一次与服务器端的往返过程。因此,非必要,应当少使用Server Control。
26. ASP.NET应用程序性能测试
在对ASP.NET应用程序进行性能测试之前,应确保应用程序没有错误,而且功能正确。具体的性能测试可以采用以下工具进行:
Web Application Strees Tool (WAS)是Microsoft发布的一个免费测试工具,可以从http://webtool.rte.microsoft.com/上下载。它可以模拟成百上千个用户同时对web应用程序进行访问请求,在服务器上形成流量负载,从而达到测试的目的,可以生成平均TTFB、平均TTLB等性能汇总报告。
Application Center Test (ACT) 是一个测试工具,附带于Visual Studio.NET的企业版中,是Microsoft正式支持的web应用程序测试工具。它能够直观地生成图表结果,功能比WAS多,但不具备多个客户机同时测试的能力。
服务器操作系统"管理工具"中的"性能"计数器,可以对服务器进行监测以了解应用程序性能。
结论
对于网站开发人员来说,在编写ASP.NET应用程序时注意性能问题,养成良好的习惯,提高应用程序性能,至少可以推迟必需的硬件升级,降低网站的成本。
常用Asp.NET程序优化方法
版权:待定点击次数:63
ASP.NET 的缓存机制相比ASP有很大的改进,本文档除对常用优化方法进行总结介绍外,强调了如何使用ASP.NET的缓存来获得最佳性能。 |
Asp.net性能优化总结
一、使用存储过程:
1. 性能方面:存储过程提供了许多标准sql语言中所没有的高级特性。其传递参数和执行逻辑表达式的功能,有助于应用程序设计者处理复杂任务。另外,存储过程存储在本地服务器上,减少了执行该过程所需的网络传输宽带和执行时间。(存储过程已经对sql语句进行了预编译,所以其执行速度比在程序里执行sql语句快很多)
2. 程序结构方面:从程序的可扩展性看,使用存储过程会对程序以后的修改带来方便。比如数据库的结构改变了,只需修改相对应的存储结构,和程序中的调用部分即可。
这部分不属于本文探讨范围,属于程序结构设计方面。所以不在此展开。
3. 程序安全性:使用存储过程可避免SQL Injection攻击。
二、查询语句的优化(针对sql server2000)
很多人只为目的写出sql语句,而不考虑sql语句的执行效率。在这我只提供一优化表顺序的方法,(sql语句的优化和原则将会在我的sql server2000学习笔记中专题讨论)
对sql语句执行效率可用sql server2000的查询分析器来查看语句的执行过程。
优化表顺序:一般情况下,sqlserver 会对表的连接作出自动优化。例如:
select name,no from A
join B on A. id=B.id
join C on C.id=A.id
where name=’wang’
尽管A表在From中先列出,然后才是B,最后才是C。但sql server可能会首先使用c表。它的选择原则是相对于该查询限制为单行或少数几行,就可以减少在其他表中查找的总数据量。绝大多数情况下,sql server 会作出最优的选择,但如果你发觉某个复杂的联结查询速度比预计的要慢,就可以使用SET FORCEPLAN语句强制sql server按照表出现顺序使用表。如上例加上:SET FORCEPLAN ON…….SET FORCEPLAN OFF 表的执行顺序将会按照你所写的顺序执行。在查询分析器中查看2种执行效率,从而选择表的连接顺序。
*使用SET FORCEPLAN选择表联结顺序
三、页面的优化(.aspx)
主要针对几个页面属性
1.EnableViewState(页面的视图状态)。如果无特殊要求设置为false。
使用ViewState ,每个对象都必须先序列化到 ViewState 中,然后再通过回传进行反序列化,因此使用 ViewState是没有代价的。尽量减少使用对象,如果可能,尽量减少放入 ViewState 中的对象的数目。下面情况基本上可以禁用viewstate:
(1)页面控件(.ascx)
(2)页面不回传给自身。
(3)无需对控件的事件处理。
(4)控件没有动态的或数据绑定的属性值(或对于每个postpack都在代码中处理)
单个页面或每个页面都禁用 ViewState,如下所示:
单个页面:<%@ Page EnableViewState="False" %>
每个页面:在 web.config 中 <Pages EnableViewState="false" />
EnableSessionState保持默认值即可(如果页面用到sessionstate它才会占用资源)。
EnableViewStateMac如果无安全上的特殊要求,保持默认值。
2.Pagelayout.页面布局模型。建议使用Flowlayout(元素不带绝对定位属性添加).Gridlayout(绝对定位属性)由于采用绝对定位,将会比Flowlayout生产更多的代码,主要是控件的定位信息。
3.项目发布的时候切记解除页面的Debug状态。
4.Html语言的优化。我的建议是熟练掌握Html/JavaScript,少用vs.net2003自动生产的代码,它会自动生成一些无用的html代码。
5. smart navigation设置为true能让用户明显的感觉性能提高。启用此属性后对客户端和服务端影响不大.它能智能涮新需要涮新需涮新的部分.
四、控件的选择:
Html控件和服务器控件的选择。服务器控件带来的方便和功能上的实现是html控件所不能比拟的。但是是以牺牲服务器端的资源来取得的。我个人建议:如果html控件达不到所要实现的功能,而且和一些脚本语言(如javascrpt/vbscript)结合也不能实现的话。才会选择服务器控件。选择服务器控件后,也尽量对其控件优化,如取消一些页面状态等(具体看控件的优化)
服务器控件的选择:主要针对几个常用数据控件说明一下:
DataGrid:自带最强大的数据显示控件,内置了对数据的修改、删除、添加、分页等很多实用功能。如果你只需对数据显示的话,尽量不要选择DataGrid(它把数据都存储在viewstate中).也不要使用自带的分页功能,microsoft在自动分页的底层做了很多工作,虽然使用方便了,但性能开销大了。(推荐一分页控件:http://webdiyer.europe.webmatrixhosting.net/default.aspx)
DataList:比DataGrid功能少了很多。但自定义性强了很多。特有的多行数据显示,给我们带来了很多方便。DataGrid能实现的功能,它基本能实现。所以建议使用它。
Repeater:功能最少,但自定义性非常强。如果只需对数据显示,建议使用。由于减少了很多功能,对服务器的性能带来消耗最小。因此,如果是对数据显示的话,我基本上都是选择Repeater然后DataList最后DataGrid
*尽量选择html控件。能在客户端实现的功能就在客户端实现(熟练掌握javascript),减少服务器的压力。数据控件选择顺序:Repeater、DataList、DataGrid
五、服务器控件的优化:
1.Viewstate
控件的viewstate与页面的viewstate基本是一致的。用来保存控件的一些状态。
处理原则和处理页面的viewstate一样。有兴趣的可以用Datagrid绑定数据测试下
viewstate保存的数据量有多大,它所保存的数据基本和Datagrid显示的数据量大小
是等同的。
2.Ispostpack
默认false.需要产生事件的时候才需设置为true.
控件的优化,主要看你对此控件的熟悉情况。对控件内部运作的原理越了解,就会对其作出合适的优化。