2011年11月5日

我的网站优化之路

背景:

以前开发的某网站的一个频道的大致情况是: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 文件(因午夜时分站点几乎无人访问)。 用户ID12345在登录后首先查找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 文件,因一个目录下的文件过多,硬盘消耗的太快,也担心会存在潜在的问题。

posted @ 2011-11-05 17:43 感恩的心 阅读(2315) 评论(13) 编辑

2011年8月15日

远程查询批量导入数据

应用背景:
     两个数据库的表结构相同,但表名前缀不一样,现要保持原有的数据表的 ID 导过来不变。
     用数据库批量导入/导出工具行不通,只能自己写SQL语句。奈何有180多个表,手工写肯定是累S个人。

我的解决办法:
     先用 OpenDataSource 远程连接服务器,然后执行插入语句可行。其语句如下:
truncate table ActUser
SET IDENTITY_INSERT ActUser on 
insert into ActUser(
[ID],[PassWord],[UserName]
select 
[ID],[PassWord],[UserName]
 
from opendatasource('SQLOLEDB','data source=DBSERVERIP;uid=sa;password=sa').TEST.dbo.ActUser
SET IDENTITY_INSERT ActUser off

    然后我想到用游标结合 sysobjects 和 syscolumns 来实现自动生成这样的语句,其代码如下:

declare mycursor cursor 
    
for
    
select [id],[name] from dbo.sysobjects where type='U'
open mycursor
declare @tablename sysname
declare @tableid sysname 
fetch next from mycursor into @tableid , @tablename -- 获取表名 
while(@@fetch_status=0)
begin
    
print '-- [' + @tablename + ']' 
    
-- 拼凑字段,将行转为字符串 --
    DECLARE @fields varchar(8000)
    
set @fields = ''
    
SELECT @fields = '' + @fields + '],[' + name  FROM syscolumns WHERE id=object_id(@tablename)  ORDER BY colid
    
set @fields = '[' + STUFF(@fields1, 3''+ ']'
    
-- 拼凑字段 End --
    --print @fields 
    /******* 避免因字段过长而导致截断情形 ********/
    
print 'truncate table ' + @tablename  --清理数据
    print 'SET IDENTITY_INSERT '+ @tablename +' on '    --可使自增长列转为可插入数据,不用重新生成 ID  
    print 'insert into ' + @tablename + '(' 
    
print @fields 
    
print ') select ' 
    
print @fields 
    
print ' from opendatasource('
            
+ '''SQLOLEDB'',''data source=DBServerIP;uid=sa;password=sa'').TEST.dbo.'
            
+ replace(@tablename ,'Wait_''Has_'
    
print 'SET IDENTITY_INSERT '+ @tablename +' off '
    
print char(13-- 换行 
    fetch next from mycursor into @tableid , @tablename
end
close mycursor
deallocate mycursor

     将生成的 SQL 语句放到查询分析器里执行一下,生成SQL 语句,然后执行一下,OK,完工。

posted @ 2011-08-15 12:50 感恩的心 阅读(87) 评论(0) 编辑

2011年2月25日

修改 MZTreeView 赋权节点父节点选中子节点自动选中的问题

以前的一个项目在给用户赋权节点的时候采用了 MzTreeView , 最近有同事给我反映说修改用户权限的时候,显示的总不正确,因为他只想给某人某一些节点下的某一些权限,但总是看到全选了。应该是他有哪些就如实是哪些的。我看到了,只要父节点被选中了,所有的子节点自动被选中。在看了看JS代码后,我找到了 system/web/ui/webcontrols/mztreeview.js 下的 312 行:

将:
node.checked=node.parentNode.checked || node.checked;
改为:
node.checked= node.checked;
问题得以解决。

posted @ 2011-02-25 09:56 感恩的心 阅读(203) 评论(0) 编辑

2011年2月16日

关于乱码的问题解决记录

最近碰到以前一个老旧项目的修改问题,其整个项目用 gb2312 编码,现要增加一个组件的应用,而这个组件又必须要求用 utf-8 编码。我在web.config 中对这个页面做了一个特殊化的utf-8处理后问题来了:另外一个功能 Post 数据都是乱码。后琢磨了一下,页面在提交前对表单的值用 JS 的 escape 编码一下后提交,服务器端处理用 Server.UrlDecode 处理一下,问题勉强得以解决。可我总觉得这样的处理非常别扭。。。。。

posted @ 2011-02-16 10:28 感恩的心 阅读(121) 评论(0) 编辑

顿悟

软件工程拿到现实生活中的时候就是:软件的实现是一个互动交流的过程-文档的目的是为了顺畅的沟通,计划的目的是保证工程的顺利实现。它提出了一套框架告诉我们如何进行有成效的沟通用以避免后续的问题,其核心还是沟通与成本。 你死套软件工程的某一个学说,如:瀑布式,原型法,如XP,SCRUM,未必哪样就适合你,现实中程序框架也是这样的嘛,你总得有些变通才能让其顺利的走下去。

posted @ 2011-02-16 09:30 感恩的心 阅读(92) 评论(0) 编辑

2011年1月12日

关于团队管理的几点观察与体会

慎用经济惩罚

所起到的作用是 杀一儆百
副作用是严重打击下属的积极性

关于工资的增长的一点体会
1.一个周期内制度性地增长不会明显地激发员工的积极性
2.从来不增加会伤害员工的积极性
3.根据实际工作所表现出来的工作能力及贡献适当地增长会激发其积极性及员工的潜能
个人认为公司所处的发展阶段不一样,其策略也不一样:
大规模又有实力的公司一般会采取1为主
迷茫的中小企业采取的方法2为主
有明确的创业方向的中小企业会采取3为主

posted @ 2011-01-12 13:18 感恩的心 阅读(132) 评论(0) 编辑

2010年12月3日

Forms 身份验证下“诡异”的Cookie 丢失问题

摘要: 背景:一年多以前本人做了一个管理系统,采用 Forms 身份验证的方式,当时系统不大,我采用了一个可序列化的对象记录了用户的用户名、ID ,用户节点ID 等信息将其序列化为一个字符串后存入到 cookie 中,这样做运行的一直良好。随着时间的推移,各个应用后台都逐步加入到了这个系统中也相安无事。前段时间我又在这个系统里加入了一个应用并将一个节点分配给我自己后做完测试就没再去理会了。前段时间因要上这...阅读全文

posted @ 2010-12-03 15:30 感恩的心 阅读(184) 评论(3) 编辑

2010年7月20日

有效的沟通,从聆听开始

摘要: 今天运行ACT以测试新项目的性能,由于是第一次用这个,对其不太熟悉,在新建测试的过程中不知道为什么它自动建立了一个代理导致上不了网,就找了运维部的同事帮忙看一下。那兄弟上来一看,IE 所有的设置正常,IP ,网关,DNS 都正常,于是断定:中毒了。我一再解释我从不上什么小网站,机器也装了个 360 ,应该不会中招。他们咔咔上来杀毒,怀疑 IE 的 dll 被篡改。。。各种方法都试了, 在3个兄弟的...阅读全文

posted @ 2010-07-20 23:02 感恩的心 阅读(150) 评论(0) 编辑

2010年7月19日

纠结的重构:事件方式?配置方式?还是其他?

摘要: 应用背景: 曾经开发过一个子系统,它最初的功能是站内的会员间相互送礼(某些礼物每个月是固定的),后来随着运营业务的扩展,逐渐地给它赋予了新的含义:比如参与评选最受欢迎的会员活动后,会员每收到一个礼物,其欢迎值按照某个比例增加,不参与活动的则忽略;某次与某个礼品店合作,会员参与活动后在特定的时间段内收到若干朵特定的花后即可兑换成某个实物;某些时候某些礼物又作为一个投票计数功能…̷...阅读全文

posted @ 2010-07-19 15:23 感恩的心 阅读(115) 评论(0) 编辑

2010年7月17日

做插件式系统的重构与反思

摘要: 本文是基于昨天的 利用反射做插件式系统的一次实践 所写的一个补充。这个系统有几个不足: 所有的插件依赖于主程序,这是一个很大的弊病;公用的基础代码没有。如日志处理、异常处理等公用的基础代码没体现,而且这个工程里到处都有相同代码的痕迹,应该予以重构 ;基于以上的考虑,我将这个 Demo 重构:增加一个 CS.Utility 工程,其作用是实现一些公用代码,如日志、异常、字符处理等,同时将 IPlug...阅读全文

posted @ 2010-07-17 22:07 感恩的心 阅读(242) 评论(1) 编辑