现在写一个的web应用程序变得非常的简单许多的程序员都不愿花时间去构建一个性能良好的应用程序本文将要讨论提高web应用程序性能的十大方法我将不限于只讨论应用程序的内容因为它们只是web应用程序的一个子集本文也不能提供一个完整提高web应用程序性能的指南因为这需要一本书的篇幅本文只提供一个提高web应用程序性能的良好的开端(剩下的只有我们自己慢慢研究了)
在工作这外我经常去攀巖在每次攀巖之前我都会重温一下攀巖线路图及看一下前面的成功的攀巖者的建议因为我们需要它们的成功经验同样的当你需要修改某个有性能问题的程序或者是要开发一个高性能的站点时你也需要学习怎么样写一个高性能的web应用程序
我个人的经验主要来源于在微软的组担任程序经理运行和管理网站和协助开发Community Server(它是 ForumsText and nGallery的集成升级版本软件)我想这些经验能我让来帮助大家
你也许会想到把你的应用程序划分成不同的逻辑层你也可能听过三层物理架构或N层架构这是最常用的架构模式它把不同的程序功能物理的分配给各个硬件来执行这样如果我们想提高应用程序的性能的话加一些硬件就可以达到目的了按理说这种方法能提高应用程序的性能但是我们应该避免使用这种方法所以只要有可能我们都应该把页面和它用到的组件放到一个应用程序中运行
因为分布式的布署要用到web services或者Remoting它将使应用程序的性能下降%或者更多
对于数据层有点不同最好还是把它独立出来布署用一个单独的硬件来运行它虽然这样但是数据库仍然是应用程序性能的瓶颈因此当你想优化你的程序的时候首先想到的地方就应该是优化数据层了
在修改应用程序的出现性能问题的地方之前你要先确认出问题的地方的程序看起来很严密性能分析器对于查找应用程序哪些地方花费了多长时间非常有用这些地方是我们用直觉感觉不到的
本文讨论两种类型的性能优化一种是大的性能优化(big optimizations)如用的Cache另一种是小的性能优化(tiny optimizations)小幅的性能优化有时候非常有用你只对你的代码作一个小的改到然后一次调用它一千或一万次作一次大的性能优化你会发生你的应用程序的速度会有一个很大的提升作一次小的性能优化也许每次请求只能提高一微秒但是如果每天的请求量很大的话那么应用程序就有很显着的性能提升
数据层的性能
当你要优化一个应用程序的性能的时候你可以按下面的顺序工作你的代码要访问数据库?如果要访问数据库频率怎么样?同样这种测试方法也可以用在用web services或Remoting的程序代码中本文将不讨论用Web services和Remoting的程序优化的问题
如果在你的代码中有一段必须访问数据库的请求而你在其它的地方又看到实现同样的功能 的代码那么你首先要优化它修改和完善继续测试除非你有一个非常大的性能问题你的时间最好花在优化查询连接数据库返回数据集的大小以及一次查询往返回的时间上
根据经验的总结让我们来看看十个能帮助你提升你的应用程序性能的经验我将按将它们提升效率的多少从大到小小依次说明
一返回多个数据集
检查你的访问数据库的代码看是否存在着要返回多次的请求每次往返降低了你的应用程序的每秒能够响应请求的次数通过在单个数据库请求中返回多个结果集可以减少与数据库通信的时间使你的系统具有扩展性也可以减少数据库服务器响应请求的工作量
如果你是用动态的SQL语句来返回多个数据集那我建议你用存储过程来替代动态的SQL语句是否把业务逻辑写到存储过程中这个有点争议但是我认为把业务逻辑写到存储过程里面可以限制返回结果集的大小减小网络数据的流量在逻辑层也不用在过滤数据这是一个好事情
用SqlCommand对象的ExecuteReader方法返回一个强类型的业务对象再调用NextResult方法来移动数据集指针来定位数据集示例一演示了一个返回多个ArrayList强类型对象的例子只从数据库中返回你需要的数据可以大大的减小你的服务器所耗用的内存
二对数据进行分页
ASPNET的DataGrid有一个非常有用的功能分页如果DataGrid允许分页在某一时刻它只下载某一页的数据另外它有一个数据分页的济览导航栏它让你可以选择浏览某一页而且每次只下载一页的数据
但是它有一个小小的缺点就是你必须把所有的数据都绑定到DataGrid中也就是说你的数据层必须返回所有的数据然后DataGrid再根据当前页过滤出当前页所需要的数据显示出来如果有一个一万条记录的结果集要用DataGrid进行分页假设DataGrid每页只显示条数据那就意味着每次请求都有条数据都是要丢弃的每次请求都要返回这么大的数据集对应用程序的性能影响是非常大的
一个好的解决方案是写一个分页的存储过程例子是一个用于对Northwind数据库orders表的分页存储过程你只需要传当前页码每页显示的条数两个参数进来存储过程会返回相应的结果
在服务器端我专门写了一个分页的控件来处理数据的分页在这里我用了第一个方法在一个存储过程里面返回了两个结果集数据记录总数和要求的结果集
返回的记录总数取决于要执行查询例如一个where条件可以限制返回的结果集的大小因为在分页界面中必须要根据数据集记录的大小来计算总的页数所以必须要返回结果集的记录数例如如果一共有条记录如果用where条件就可以过滤成只返回条记录存储过程的分页逻辑应该知道返回那些需要显示的数据
三连接池
用TCP来连接你的应用程序与数据库是一件昂贵的事情(很费时的事情)微软的开发者可以通过用连接池来反复的使用数据库的连接比起每次请求都用TCP来连一次数据库连接池只有在不存在有效的连接时才新建一个TCP连接当关闭一个连接的时候它会被放到池中它仍然会保持与数据库的连接这样就可以减少与数据库的TCP连接次数
当然你要注意那些忘记关的连接你应在每次用完连接后马上关闭它我要强调的是无论什么人 framework中的GC(垃圾收集器)总会在你用完连接对象后调用连接对象的Close或者Dispose方法显式的关闭你的连接不要期望CLR会在你想象的时间内关掉连接虽然CLR最终都要销毁对象和关闭边接但是我们并不能确定它到底会在什么时候做这些事情
要用连接池优化有两条规则第一打开连接处理数据然后关闭连接如果你必须在每次请求中多次打开或关闭连接这好过一直打开一个边接然后把它传到各个方法中第二用相同的连接字符串(或者用相同的用户标识当你用集成认证的时候)如果你没有用相同的连接字符串如你用基于登录用户的连接字符串这将不能利用连接池的优化功能如果你用的是集成的论证因为用户很多所以你也不能充分利用连接池的优化功能NET CLR提供了一个数据性能计数器它在我们需要跟蹤程序性能特性的时候非常有用当然也包括连接池的跟蹤了
无论你的应用程序什么时候要连在另一台机子的资源如数据库你都应该重点优化你连资源所花的时间接收和发送数据的时间以及往返回之间的次数优化你的应用程序中的每一个处理点(process hop)它是提高你的应用的性能的出发点
应用程序层包含与数据层连接传送数据到相应的类的实例以及业务处理的逻辑例如在Community Server中要组装一个Forums或者Threads集合然后应用业务逻辑如授权更重要的这里要完成缓存逻辑
四 ASPNET缓存API
在写应用程序之前你要做的第一件事是让应用程序最大化的利用ASPNET的缓存功能
如果你的组件是要在应用程序中运行你只要把SystemWebdll引用到你的项目中就可以了然后用HttpRuntimeCache属性就可访问Cache了(也可以通过PageCache或HttpContextCache访问)
有以下几条缓存数据的规则第一数据可能会被频繁的被使用这种数据可以缓存第二数据的访问频率非常高或者一个数据的访问频率不高但是它的生存周期很长这样的数据最好也缓存起来第三是一个常常被忽略的问题有时候我们缓存了太多数据通常在一台X的机子上如果你要缓存的数据超过M的话就会出现内存溢出的错误所以说缓存是有限的换名话说你应该估计缓存集的大小把缓存集的大小限制在以内否则它可能会出问题在中如果缓存过大的话也会报内存溢出错误特别是如果缓存大的DataSet对象的时候
这里有几个你必须了解的重要的缓存机制首先是缓存实现了最近使用原则( a leastrecentlyused algorithm)当缓存少的时候它会自动的强制清除那些无用的缓存其次 条件依赖强制清除原则(expiration dependencies)条件可以是时间关键字和文件以时间作为条件是最常用的在中增加一更强的条件就是数据库条件当数据库中的数据发生变化时就会强制清除缓存要更深入的了解数据库条件依赖请看Dino Esposito 在MSDN杂志年七月刊的Cutting Edge专栏文章的缓存架构如下图所示
五 预请求缓存
在前面我提到过即使我们只对某些地方作了一个小小的性能改进也可以获得大的性能提升我非常喜欢用预请求缓存来提