[原] JT SQL Server 性能调优札记之四

真凶

谨慎起见,我并没有马上将索引应用到生产的JT数据库上面,晚上12点,再次登陆数据库服务器,查看以下CPU的负载,把我吓了一跳。负载还是那么高,按我们的经验晚上该数据库服务应该是非常空闲的,为什么还会有这么大的负载呢。

image_thumb_4

马上打开活动监视器,发现结果还是和以前的一样,还是jt_user的用户CPU和IO最高,但是晚上jt_user对应的应用系统应该没有人用。

马上打开SQL Profiler,事件仅选BatchCompleted,将CPU的阀值设为5,Duration的阀值设为10,这个阀值其实已经很低的了,也就晚上12点我才敢这么设定。

结果收集的结果吓我一跳,

image

在短短的13分钟内,竟然同一条语句执行了1479次。

select top 1 end_time from Log_Network_circs order by end_time desc

Log_Network_circs这张表有2657563条记录,平均每秒查询1.89次,每分钟查询113.8次,说白了就是秒对一个265万条记录的表进行排序。而且这条语句明显能写成:

select max(end_time) from Log_Network_circs

又短效率比原来的还高,想不明白。以下是统计信息。

表 'Log_Network_circs'。扫描计数 3,逻辑读取 16352 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server 执行时间:
   CPU 时间 = 1156 毫秒,占用时间 = 619 毫秒。

更要命的是,我发现这个表的记录数是时刻增长的,每秒4~5条。insert 语句肯定肯定是让我那个这么低的阀值过滤掉了。

这个明显属于应用系统设计的问题,找开发商聊了。

 

不是办法的办法

与开发商联系后,问清楚情况,确认该表的数据可以删除,以防万一,将数据库备份,然后truncate了这个表。但是这个不是一个最好的解决办法,我个人认为该问题应该在应用层上面解决。我向开发商提出了以下几点建议:

1。将查询的频率调低一点。
2。select max(end_time) from Log_Network_circs 能比原来这条语句性能好很多,如果只是为了查最后一条。
3。直接写个触发器,在插入的时候把end_time存到别的表里面去,然后在这个表里面取end_time的值。
4。定时执行清空这个表的数据。

Tag标签: Sql Server 2005
0
0
(请您对文章做出评价)
« 上一篇:[原] JT SQL Server 性能调优札记之三
» 下一篇:[原]发布一个防SQL注入漏洞的HttpModule
posted @ 2009-02-20 13:18 killkill 阅读(2406) 评论(23)  编辑 收藏 网摘 所属分类: SQL Server 实践

  回复  引用  查看    
#1楼2009-02-20 13:29 | RawMan      
佩服作者,向你学习
我还以为是数据库本身的问题呢,我还想,sql server在空闲的时候是会多占但内存的,但没听说过占CPU啊,后面那句sql语句,更奇怪了,ms的人难道会让系统一直执行这个?

最后……faint……

Ps:服务器配置不错。

  回复  引用  查看    
#3楼[楼主]2009-02-20 13:59 | killkill      
@LeadNT.NET - 无技术,不网摘
呵呵,的确很Faint,上一篇中使用SQL Profiler的阀值调得很大,没有纠住这个语句,实在太失败了。

服务器还行吧,三年前的服务器了,正准备升级到一台8核16G的机器。

  回复  引用    
#4楼2009-02-20 15:20 | lidong6[未注册用户]
你在end_time列个创建个索引不就解决了吗,建索引后这两个查询的逻辑读取数应该在10之内吧。

另外,理论上,这两个查询的效率应该是一样的,甚至它们的执行计划也应该非常接近。

除非你测试的方法有问题。

  回复  引用  查看    
#5楼2009-02-20 15:47 | rO8eR70.nEt      
--引用--------------------------------------------------
lidong6: 你在end_time列个创建个索引不就解决了吗,建索引后这两个查询的逻辑读取数应该在10之内吧。

另外,理论上,这两个查询的效率应该是一样的,甚至它们的执行计划也应该非常接近。

除非你测试的方法有问题。
--------------------------------------------------------

呵呵,你这个建索引的方法我也十分赞成!

  回复  引用  查看    
#6楼2009-02-20 16:42 | 巴山游子      
在不改系统的情况下:
1、在 end_time 列上建立一个 倒序 排列的 聚簇 索引。
2、后台加一个Job ,定期归档数据。




  回复  引用  查看    
#7楼2009-02-20 17:17 | Tianjon      
说实话,200多万的数据,并发每秒4-5条的插入,1.89次的查询。这样的并发并不算高... 另外你的两种查询的写法对于SQL SERVER 2005 而言,他应该会选用相同的执行计划... .

从文章看,你好像在用Profile 分析跟踪结果,建议用下ReadTrace,他处理完之后你在报表上应该很容易看出问题。

建议:
1、如果对数据的一致性不高,在选择时,可以将事物的隔离级别设置为“未提交读”,这样读写不阻塞,有助于提高系统的并发量。
2、在Log_Network_circs 表end_time列创建一个非聚集索引,根据目前数据量,在该列增加索引后,即使数据达到千万,一次查询应该不超过4次IO

如果考虑系统的持久稳定,建议将日志信息分表放,可以每天存放一个表,只是要用动态语句实现相关功能。



  回复  引用  查看    
#8楼[楼主]2009-02-20 17:32 | killkill      
@巴山游子
第一个提议很好,但是使用以下语句
select count(end_time),end_time from Log_Network_circs
group by end_time
having count(end_time)>1
查询,发现end_time是有重复的,所以建不了聚集索引。

第二个提议也很好,估计最有可能通过的方案。

  回复  引用  查看    
#9楼2009-02-20 17:35 | Tianjon      
@killkill
1、没有必要建聚集索引,非聚集即可,因为你这个查询不涉及其他列
2、聚集索引可以不是唯一的,重复不重复没关系的

  回复  引用  查看    
#10楼2009-02-20 17:36 | 徐少侠      
每秒有4-5条的插入

做倒序索引是可以解决问题

不过
直接写个触发器,在插入的时候把end_time存到别的表里面去,然后在这个表里面取end_time的值
这个貌似跟舒服一点。
毕竟建这么个索引仅仅为了获得最后一个值,感觉不爽。
虽然更新操作会慢点,不过也就每秒4-5次的。索引也一样要非点时间的。

  回复  引用  查看    
#11楼2009-02-20 17:36 | Tianjon      
相反,对这个查询建聚集索引的话效率并没有非聚集的高,你可以看看 呵呵
  回复  引用  查看    
#12楼2009-02-20 18:39 | 周强      
--引用--------------------------------------------------
killkill: @巴山游子
第一个提议很好,但是使用以下语句
select count(end_time),end_time from Log_Network_circs
group by end_time
having count(end_time)>1
查询,发现end_time是有重复的,所以建不了聚集索引。

建立聚集索引的时候,忽略掉重复值就可以了。


  回复  引用  查看    
#13楼2009-02-20 18:50 | 周强      

两条SQL语句,只要以end_time建立索引,执行效率都是很好的。聚集于非聚集差距应该相当小。

  回复  引用  查看    
#14楼[楼主]2009-02-20 18:53 | killkill      
@周强
嗯,是的,我眼花了,看成唯一聚集索引了,是我的失误。

  回复  引用  查看    
#15楼2009-02-20 19:22 | 周强      
--引用--------------------------------------------------
Tianjon: 相反,对这个查询建聚集索引的话效率并没有非聚集的高,你可以看看 呵呵
--------------------------------------------------------


非聚集如果已经包含了所有要查询的数据,一般情况来说,覆盖索引是效率最好的。但是就该例来说,不论是聚集还是非聚集,SQL都只会将聚集索引页级的最后一页或者将非聚集索引的最后一个索引页进行IO.也就是说,聚集与非聚集的效率如果从IO方面来说,差距在于聚集索引和非聚集的级别数。

  回复  引用  查看    
#16楼2009-02-20 19:43 | Damon King      
博主,为啥说MAX相对于Top 1语句的性能好呢,我在64万条数据的情况下测试了,包括查询分析与I/O分析,二者相同啊!
  回复  引用  查看    
#17楼[楼主]2009-02-20 19:57 | killkill      
@Damon King
我记得很久以前在csdn上面看过一篇贴是关于这个问题的讨论,查看查询计划,max是比top 1的性能好至于执行的时候为什么会一样这个我也没有弄清,不过你说的的确事实,我在2000和2005都得到你所说的结果。

  回复  引用  查看    
#18楼[楼主]2009-02-20 21:07 | killkill      
LS的各位:
非常感谢大家的回复,我承认是我的问题:只看执行计划,没看执行的统计结果。
请大家看看这篇大作:
http://www.cnblogs.com/oldhorse/archive/2007/05/31/766641.html
http://blog.darkthread.net/blogs/darkthreadtw/archive/2007/03/29/678.aspx
里面的结论貌似说明 max 比 top 1 的效率高。

@Tianjon
------------------------------------------------------
说实话,200多万的数据,并发每秒4-5条的插入,1.89次的查询。
------------------------------------------------------
这个的确不算高,由于我对大家提供的信息太少,其实不只该条语句平均每秒1.89次查询,而是有差不多20条语句以这个频率,我个人认为该业务系统只是为了监控另一端的通断情况,而作出如此设计非常不合理的设计,我感到非常气愤。
愤怒蒙蔽了双眼,下了一个错误结论,是我的失误。

非常感谢大家对我的指导。

  回复  引用  查看    
#19楼2009-02-20 21:12 | 巴山游子      
@killkill
有重复值也可以建 聚集 索引啊!

  回复  引用  查看    
#20楼[楼主]2009-02-20 21:18 | killkill      
@巴山游子
嗯,是的,我眼花了,看成唯一聚集索引了,是我的失误。

  回复  引用  查看    
#21楼2009-02-21 16:07 | Keep Walking      
这个表很重要的话建议不要滥用包含行索引,如果是日志型表,每个查询都与时间相关,最好把聚集索引建在时间上,无论是插入,选取,都是最优的查询方案,而且不需要其他而外的索引。超大型表在建索引的时候要非常慎重的考虑,不是因为一个查询就要加索引,这种行为对DBA来说是不可接受的。
另外,把时间做聚集索引,可以为以后很好的做扩展,数据大了,直接用时间做分区。

  回复  引用  查看    
#22楼[楼主]2009-02-21 16:37 | killkill      
@Keep Walking
非常感谢您的回复,您的这句话:“超大型表在建索引的时候要非常慎重的考虑,不是因为一个查询就要加索引,这种行为对DBA来说是不可接受的。”非常经典,非常值得所有DBA学习。
由于这个表是一个无关紧要的Log表,但是应用系统却需要如此频繁地查询该表,为的仅仅是一个很简单的目的。
站在DBA的角度来说,大家的建议已经可以证明大家都是非常优秀和称职的DBA。
但我不仅是个DBA,还是管理者,我有权力和义务通过各种方式保护我的数据库及系统,回到这个问题上,的确可以从技术上满足这种无理的需求,但是,作为一名管理员来说,对于这种无理的需求,我更偏重于去PK应用系统及其开发者。

我并没有埋怨您的意思,我只是把一些我没有说明的情况说出来,我不想做一个凡事逆来顺受的管理者,我非常讨厌对任何无理要求都要点头哈腰。

  回复  引用  查看    
#23楼2009-02-21 18:28 | Keep Walking      
我也不是DBA,只是在一个小公司里,什么都做什么都管