最近在一个比较古老的项目中,使用的是sql server 2000(sp4) 企业版,是用DotNet2.0开发的WinForm项目。实施后,运行刚开始,一直很正常,但是过了一段时间后,可能是当数据达到一定量的时候,出现一个奇怪的问题:数据库连接不上了!
于是,想在数据库服务器上来查找原因,发现在服务器上也无法连接到数据库。一样的出现“连接超时错误”,更晕的是当我想停止数据库服务时,发现显示的状态是”停止“,但是“服务管理器”下面的状态一直显示“正在停止...", 而上面的“开始”,“暂停”,“停止”三个按钮都是灰色无法点击的状态。查看进程,发现”sqlserver.exe"这个进程还在,并且使用的cpu一会是0一会跳到25,说明它还在运行!实在是没辙了,重启机器!
重启后,数据库可以访问了,貌似恢复正常。但是过了半天,又出现上面的情况,实在是太郁闷了!数据库日志也没有记录什么有用的信息,关键是无法连接到数据库,问题都没法查!换台数据库服务器试试看,结果也是问题依旧!
凭感觉,应该是sql2000里面的进程出现了阻塞或者死锁。阻塞在数据库服务中是常事,但是一般情况下只是速度慢而已,很少会连数据库整个挂掉。应该是死锁!
难道是程序中连接没有及时断开导致的?于是检查了程序,发现连接都是在访问之后都已经close掉了,这就比较棘手了!通过sp_who等检查性能,也没有发现任何有价值的线索。
这样的问题大概出现了十多天,问题查找一直无果,每次出现问题只有重启。用户一大堆的埋怨不说,自己也心力交瘁!
这样下去肯定不是办法,于是从前端操作下手,先找出出现问题的时机。通过操作此系统N多功能,最后发现了每次出现问题都是在一个打印操作完之后,另外一个人如果再次打印就会出现“连接超时错误”,但是第三次打印又可以连接到数据库,如此循环下去,最后就出现任何连接到数据库的操作都超时。这问题太奇怪了!只有去看打印功能的源代码了。经过Debug,发现:出现超时错误时,都是在执行下面这个SQL语句时出现的:
SELECT EMPID,MONTH(OVERDATE),SUM(OVERHOURS) AS MONTHSUM ,(SELECT SUM(OVERHOURS) AS YEARSUM FROM OVERINPUT WHERE EMPID=OVERINPUT.EMPID AND YEAR(OVERDATE)=2012 ) AS YEARSUM FROM OVERINPUT WHERE MONTH(OVERDATE) BETWEEN 1 AND 6 GROUP BY EMPID,MONTH(OVERDATE) ORDER BY EMPID,MONTH(OVERDATE)
这是一个打印加班统计信息的SQL,每次执行打印功能,系统按照部门数量循环(for)调用了20次左右,以打出每个部门的加班统计情况。下面是SQL中涉及到的表和字段的含义:
OVERINPUT(加班记录表) ,EMPID(员工号),OverDate(加班日期) OverHours(加班小时数)
所以上面的SQL是统计每个员工1-6月中每个月的加班总数,还有当年(2012)的加班总数,作者的目的是不想重新再查一遍年度的合计加班数,把它们混在一起
。现在这条查询语句不定时的查询超时,先优化一下:那最直接的肯定是将YEARSUM这段代码用简单的常量代替处理:
SELECT EMPID,MONTH(OVERDATE),SUM(OVERHOURS) AS MONTHSUM /* ,(SELECT SUM(OVERHOURS) AS YEARSUM FROM OVERINPUT WHERE EMPID=OVERINPUT.EMPID AND YEAR(OVERDATE)=2012 ) AS YEARSUM */ ,1 AS YEARSUM FROM OVERINPUT WHERE MONTH(OVERDATE) BETWEEN 1 AND 6 GROUP BY EMPID,MONTH(OVERDATE) ORDER BY EMPID,MONTH(OVERDATE)
当然,这样的最后打印出来的结果肯定不对,反正先测试着,看数据库还会不会死?让人意想不到的是,结果数据库一直都正常!这也太奇怪了!即使查询速度效率低,但是为什么会连数据库都死掉了呢?还有上面的方法问题是解决了,但是效率太低!类似的问题,要怎么从数据库上查找原因? 也希望有SQL牛人能指点迷津!