专栏名称: 数据分析与开发
伯乐在线旗下账号,分享数据库相关技术文章、教程和工具,另外还包括数据库相关的工作。偶尔也谈谈程序员人生 :)
目录
相关文章推荐
AustinDatabases  ·  OceanBase ... ·  17 小时前  
AustinDatabases  ·  OceanBase ... ·  17 小时前  
macrozheng  ·  300 秒到 4 秒,如何将 MySQL ... ·  昨天  
数据中心运维管理  ·  探索数据中心的多模光纤距离限制 ·  5 天前  
数据中心运维管理  ·  DeepSeek加速大马数据中心发展 ·  4 天前  
程序员鱼皮  ·  MyBatis 批量操作的 5 ... ·  2 天前  
程序员鱼皮  ·  MyBatis 批量操作的 5 ... ·  2 天前  
51好读  ›  专栏  ›  数据分析与开发

程序猿是如何解决 SQLServer 占 CPU 100%

数据分析与开发  · 公众号  · 数据库  · 2016-09-03 22:19

正文

(点击 上方公众号 ,可快速关注)


来源:马非码

链接: www.cnblogs.com/marvin/p/ASolutionForSQLServerCauseHighCPU.html


遇到的问题


有同事反应服务器CPU过高,一看截图基本都是100%了,my god,这可是大问题,赶紧先看看。



让同事查看系统进程,发现是SQLServer的CPU占用比较高。首先想到的是不是报表生成的时候高,因为这块之前出现过问题,关掉服务程序,还是高。难道是客户端程序引发的?但是这么多的客户端连接,难不成每个都叫人关闭,很简单,把网络断开即可。网络断开之后,CPU立马下降。那么问题到底在哪里呢,是时候祭出我们的利器了——SQLServer Profiler。


使用SQLServer Profiler监控数据库


让同事使用SQLProfiler监控了大概20分钟左右,然后保存为跟踪文件*.rtc。



我们来看看到底是哪句SQL有问题:


SQL1:查找最新的30条告警事件


select top 30 a.orderno,a.AgentBm,a.AlarmTime,a.RemoveTime,c.Name as AddrName,b.Name as MgrObjName,a.Ch,a. Value ,a.Content,a.Level

,ag.Name as AgentServerName,a.EventBm,a.MgrObjId,a.Id,a.Cfmoper,a.Cfm,a.Cfmtime,a.State,a.IgnoreStartTime,a.IgnoreEndTime

,a.OpUserId,d.Name as MgrObjTypeName,l.UserName as userName,f.Name as AddrName2

from eventlog as a left join mgrobj as b on a.MgrObjId = b.Id and a.AgentBm = b.AgentBm

left join addrnode as c on b.AddrId = c.Id left join mgrobjtype as d on b.MgrObjTypeId = d.Id

left join eventdir as e on a.EventBm = e.Bm left join agentserver as ag on a.AgentBm = ag.AgentBm

left join loginUser as l on a.cfmoper = l.loginGuid left join addrnode as f on ag.AddrId = f.Id

where ((MgrObjId in (

select Id from MgrObj

where AddrId in ( '' , '02100000' , '02113000' , '02113001' , '02113002' , '02113003' , '02113004'

, '02113005' , '02113006' , '02113007' , '02113008' , '02113009' , '02113010' , '02113011' , '02113012'

, '02113013' , '02113014' , '02113015' , '02113016' , '02113017' , '02113018' , '02113019' , '02113020'

, '02113021' , '02113022' , '02113023' , '02113024' , '02113025' , '02113026' )))

or (mgrobjid in ( '00000000-0000-0000-0000-000000000000' , '00000000-0000-0000-0000-000000000000'

, '00000000-0000-0000-0000-000000000000' , '11111111-1111-1111-1111-111111111111'

, '11111111-1111-1111-1111-111111111111' ))

)

order by alarmtime DESC


SQL2:获取当前的总报警记录数


select count ( * ) from eventlog as a left join mgrobj as b on a . MgrObjId = b . Id and a . AgentBm = b . AgentBm

left join addrnode as c on b . AddrId = c . Id left join mgrobjtype as d on b . MgrObjTypeId = d . Id

left join eventdir as e on a . EventBm = e . Bm

where MgrObjId in (

select Id from MgrObj where AddrId in

( '' , '02100000' , '02100001' , '02100002' , '02100003' , '02100004' , '02100005' , '02100006' , '02100007'

, '02100008' , '02100009' , '02100010' , '02100011' , '02100012' , '02100013' , '02100014' , '02100015'

, '02100016' , '02100017' , '02100018' , '02100019' , '02101000' , '02101001' , '02101002' , '02101003'

, '02101004' , '02101005' , '02101006' , '02101007' , '02101008' , '02101009' , '02101010' , '02101011' , '02101012'

, '02101013' , '02101014' , '02101015' , '02101016' , '02101017' , '02101018' , '02101019' , '02101020' , '02101021'

, '02101022' , '02101023' , '02101024' , '02101025' , '022000' , '022001' , '022101' , '022102' , '0755' , '0755002' )

)

and mgrobjid not in (

'00000000-0000-0000-0000-000000000000' , '00000000-0000-0000-0000-000000000000' , '00000000-0000-0000-0000-000000000000'

, '11111111-1111-1111-1111-111111111111' , '11111111-1111-1111-1111-111111111111' )


这是典型的获取数据并分页的数据,一条获取最新分页记录总数,一条获取分页记录,正是获取最新事件这里导致的CPU过高。这里的业务大概是每个客户端,每3秒执行一次数据库查找,以便显示最新的告警事件。好了,元凶找到了,怎么解决?


有哪些SQL语句会导致CPU过高?


上网查看了下文章,得出以下结论:


1.编译和重编译


编译是 Sql Server 为指令生成执行计划的过程。Sql Server 要分析指令要做的事情,分析它所要访问的表格结构,也就是生成执行计划的过程。这个过程主要是在做各种计算,所以CPU 使用比较集中的地方。


执行计划生成后会被缓存在 内存中,以便重用。但是不是所有的都可以 被重用。在很多时候,由于数据量发生了变化,或者数据结构发生了变化,同样一句话执行,就要重编译。


2.排序(sort) 和 聚合计算(aggregation)


在查询的时候,经常会做 order by、distinct 这样的操作,也会做 avg、sum、max、min 这样的聚合计算,在数据已经被加载到内存后,就要使用CPU把这些计算做完。所以这些操作的语句CPU 使用量会多一些。


3.表格连接(Join)操作


当语句需要两张表做连接的时候,SQLServer 常常会选择 Nested Loop 或 Hash 算法。算法的完成要运行 CPU,所以 join 有时候也会带来 CPU 使用比较集中的地方。


4.Count(*) 语句执行的过于频繁


特别是对大表 Count() ,因为 Count() 后面如果没有条件,或者条件用不上索引,都会引起 全表扫描的,也会引起 CPU 的大量运算


大致的原因,我们都知道了,但是具体到我们上述的两个SQL,好像都有上述提到的这些问题,那么到底哪个才是最大的元凶,我们能够怎么优化?


查看SQL的查询计划


SQLServer的查询计划很清楚的告诉了我们到底在哪一步消耗了最大的资源。 我们先来看看获取top30的记录:



排序竟然占了94%的资源。原来是它!同事马上想到,用orderno排序会不会快点。先把上述语句在SQLServer中执行一遍,清掉缓存之后,大概是2~3秒,然后排序字段改为orderno,1秒都不到,果然有用。但是orderno的顺序跟alarmTime的顺序是不完全一致的,orderno的排序无法替代alarmTime排序,那么怎么办?


我想,因为选择的是top,那么因为orderno是聚集索引,那么选择前30条记录,可以立即返回,根本无需遍历整个结果,那么如果alarmTime是个索引字段,是否可以加快排序?


选择top记录时,尽量为order子句的字段建立索引


先建立索引:


IF NOT EXISTS ( SELECT * FROM sysindexes WHERE id = OBJECT_ID( 'eventlog' ) AND name= 'IX_eventlog_alarmTime' )

CREATE NONCLUSTERED INDEX IX_eventlog_alarmTime ON dbo.eventlog(AlarmTime)


在查看执行计划:



看到没有,刚才查询耗时的Sort已经消失不见了,那么怎么验证它能够有效的降低我们的CPU呢,难道要到现场部署,当然不是。


查看SQL语句CPU高的语句


SELECT







请到「今天看啥」查看全文