我的网站优化之路
背景:
以前开发的某网站的一个频道的大致情况是:windows server 2003+sql server 2000 + iis 6,用户数据量是200W,数据库文件约35G, 图片若干十万,日访问量10W左右;硬件是5年前中端配置的Web server 和sql server 分离的俩8G内存的服务器,因年代久远且公司硬件投入不足,这两个服务器上还分别架着很多其他的应用,CPU使用率是大部分时间保持在40%以上;以前因为访问时候偶尔出现死锁问题导致这个频道打开变慢,直到去年这个问题才逐步变得频繁发生。当时运维部门的同时一直在向领导反映希望增加硬件投入且得到了口头答应,我们也一直寄希望于在升级硬件后,这些问题的出现几率大大减少。 但新的服务器迟迟不来,网站一直在线运营,且运营部门的同事一直在向我们抱怨网站打开太慢,迫于各方面的现实问题,我开始想办法尝试在现有的条件下通过各种办法对其进行优化。
解决问题的过程:
在着手开始优化前,需开始对关键业务点进行分析,如哪些地方是用户最常用到的功能,集中访问的时间段是发生什么时候。通过Sql Server 跟踪器分析,分析IIS日志等方法确定了几个常用的功能点,然后有针对性地进行优化(我一直觉得网站性能优化是有针对性的,在实际生产环境中,你的某个点的使用方案处理得当的话,远比你冥思苦想地优化某几个方法使程序运行100W次性能得到几百个微秒级的提升来得有意义,况且实际网站运营的过程中,如果一天运行100W次的话,所节省的这几百微秒的提升几乎可以忽略不计)。
在确定关键业务点后,发现这些关键业务点的主要压力是来自数据库,为此需要做缓存。第一阶段 选择的是.net自带的HttpRuntime.Cache。其方法是如果涉及到页面需从数据库中取数据,首先看cache 中有没有这个数据,如果有则直接返回,没有则先从数据库中取出然后放入HttpRuntime.Cache中,并设置10分钟后过期-这个时间如果过短,则缓存做的几乎没什么意义,如果过长则数据的实时性让人难接受。在某些关键的地方则从数据的实时性和访问速度上做一个权衡,服务器上做一个定时程序使之静态化。使用这个版本的优化后监测到服务器上的内存一直维持在 6G 以上,运行了一段时间后速度勉强还能接受,但数据的实时性一直是我们在回避的问题,访问速度也不尽如意,怀疑是Cache的命中率问题,但没仔细验证过。
在第二阶段:采用 DataSet.WriteXML() 和 DataSet.ReadXML() 的方式做缓存这个方案运行了一段时间,发现经常出现IO读写冲突的错误,某些常访问的缓存很难更新到。于是采用HttpRuntime..Cache + XML + 服务器端定时删除XML的方式 做缓存,如果内存中没数据,则首先尝试读XML文件缓存,如果XML文件缓存也没有则读数据库的方式,这样解决了XML文件经常删不了的问题,也附带地解决了经常性的内存缓存失效带来的数据库负担没减轻的问题。这个时期的一个升级版本是内存缓存+文件依赖的方式实现缓存失效。
第三阶段:采用HttpRuntime.Cache + SQLite 的方式实现。在第二阶段发现的一个问题是:如果某些地方数据量过大,使其写入XML的数据使单个文件超过2MB,则读取的速度会很慢很慢,还不如直接查库来的快,这样的话文件缓存就失去了意义。 其大致过程是:定义活跃用户-若干小时内登陆过的用户则为活跃用户,将活跃用户常用到的数据在午夜写入到其对应的单个 SQLite 文件中,同时删除24小时以前的所有SQLite文件。 比如用户ID为 12345 的活跃用户在每天午夜会生成文件 d:\e\12345.sqlite 文件(因午夜时分站点几乎无人访问)。 用户ID:12345在登录后首先查找d:\e\12345.sqlite 该文件,如存在则直接读取此文件内的数据,如不存在则查库后采用阶段一的缓存方案。实际运行一段时间后,发现 SQLite 里单表存 1000多条数据访问起来也没什么问题,且相比XML来说,相同的数据它的体积明显比XML 小-这里赞一下SQLite,它在某些地方确实很有用。此时我将多个应用模块根据用户加入到该库中。这样一来,SQL Server 再也没有那么忙了,忙的就是Web Server 的硬盘了。
由于在开发初期采用了Microsoft Enterprise Library 4.1 库,第三阶段整个优化过程中没费多少周折。至此终于可以面对数据的实时性的问题了。这个时期的一个升级版是:用户123在向用户456写一条消息时,先写到SQL Server 中,然后在分别写入 123.sqlite 和 456.sqlite 中,再加上 HttpRuntime.Cache + 文件缓存依赖。更新数据也是一样。此时数据的实时性解决了,SQL Server 也终于闲下来了,网站访问速度也终于上去了。运行一段时间后在此基础上的又一个改进版是:定时删除非活跃用户的 Sqlite 文件,因一个目录下的文件过多,硬盘消耗的太快,也担心会存在潜在的问题。