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 TOP 10 TEXT AS 'SQL Statement'
,last_execution_time AS 'Last Execution Time'
,(total_logical_reads + total_physical_reads + total_logical_writes) / execution_count AS [Average IO]
,(total_worker_time / execution_count) / 1000000.0 AS [Average CPU Time (sec)]
,(total_elapsed_time / execution_count) / 1000000.0 AS [Average Elapsed Time (sec)]
,execution_count AS "Execution Count",qs.total_physical_reads,qs.total_logical_writes
,qp.query_plan AS "Query Plan"
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.plan_handle) st
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
ORDER BY total_elapsed_time / execution_count DESC
我们把建索引前后CPU做个对比:
已经明显减低了。
通过建立相关索引来减少表扫描
我们再来看看count(*)这句怎么优化,因为上面的这句跟count这句差别就在于order by的排序。老规矩,用查询计划看看。
用语句select count(0) from eventlog一看,该表已经有20多w的记录,每次查询30条数据,竟然要遍历这个20多w的表两次,能不耗CPU吗。我们看看是否能够利用相关的条件来减少表扫描。很明显,我们可以为MgrObjId建立索引:
CREATE NONCLUSTERED INDEX IX_eventlog_moid ON dbo.eventlog(MgrObjId)
但是无论我怎么试,都是没有利用到索引,难道IN子句和NOT IN子句是没法利用索引一定会引起表扫描。于是上网查资料,找到桦仔的文章,这里面有解答:
SQLSERVER对筛选条件(search argument/SARG)的写法有一定的建议
对于不使用SARG运算符的表达式,索引是没有用的,SQLSERVER对它们很难使用比较优化的做法。非SARG运算符包括
NOT、、NOT EXISTS、NOT IN、NOT LIKE和内部函数,例如:Convert、Upper等
但是这恰恰说明了IN是可以建立索引的啊。百思不得其解,经过一番的咨询之后,得到了解答:
不一定是利用索引就是好的,sqlserver根据你的查询的字段的重复值的占比,决定是表扫描还是索引扫描
有道理,但是我查看了下,重复值并不高,怎么会有问题呢。
关键是,你select的字段,这个地方使用索引那么性能更差,你select字段 id,addrid,agentbm,mgrobjtypeid,name都不在索引里。
真是一语惊醒梦中人,缺的是包含索引!!!关于包含索引的重要性我在这篇文章《我是如何在SQLServer中处理每天四亿三千万记录的》已经提到过了,没想到在这里又重新栽了个跟头。实践,真的是太重要了!
通过建立包含索引来让SQL语句走索引
好吧,立马建立相关索引:
IF NOT EXISTS(SELECT * FROM sysindexes WHERE id=OBJECT_ID('eventlog') AND name='IX_eventlog_moid')
CREATE NONCLUSTERED INDEX IX_eventlog_moid ON dbo.eventlog(MgrObjId) INCLUDE(EventBm,AgentBM)
我们再来看看查询计划:
看到没有,已经没有eventlog表的表扫描了。我们再来比较前后的CPU:
很明显,这个count的优化,对查询top的语句依然的生效的。目前为止,这两个查询用上去之后,再也没有CPU过高的现象了。
其他优化手段
通过服务端的推送,有事件告警或者解除过来才查询数据库。
优化上述查询语句,比如count(*)可以用count(0)替代——参考《SQL开发技巧(二)》
优化语句,先查询出所有的MgrObjId,然后在做连接
为管理对象、地点表等增加索引
添加了索引之后,事件表的插入就会慢,能够再怎么优化呢?可以分区建立索引,每天不忙的时候,把新的记录移入到建好索引的分区
当然,这些优化的手段是后续的事情了,我要做的事情基本完了。
总结
服务器CPU过高,首先查看系统进程,确定引发CPU过高的进程
通过SQLServer Profiler能够轻易监控到哪些SQL语句执行时间过长,消耗最多的CPU
通过SQL语句是可以查看每条SQL语句消耗的CPU是多少
导致CPU高的都是进行大量计算的语句:包括内存排序、表扫描、编译计划等。
如果使用Top刷选前面几条语句,则尽量为Order By子句建立索引,这样可以减少对所有的刷选结果进行排序
使用Count查询记录数时,尽量通过为where字句的相关字段建立索引以减少表扫描。如果多个表进行join操作,则把相关的表连接字段建立在包含索引中
通过服务端通知的方式,减少SQL语句的查询
通过表分区,尽量降低因为添加索引而导致表插入较慢的影响
参考文章
SQLSERVR语句 in和exists哪个效率高本人测试证明
Sql Server Cpu 100% 的常见原因及优化
SQLSERVER排查CPU占用高的情况
人人都是 DBA(XII)查询信息收集脚本汇编
最后,感谢博客园DBA桦仔的热心指点。
sql索引的填充因子多少最好,填充因子的作用?
在两个表都有聚集索引情况下 逻辑读最小 每个表只有 到 次 而且只有实际需要输出的数据才会被读入内存页 两个表都有非聚集索引情况下 消耗的逻辑读和内存资源近似
测试 执行Merge Join
执行全表选取或者低选择性选取
执行SQL:
以下是引用片段 Select?*?From?P_Order?A Inner?merge?JOIN?P_User?B?ON?A MobileNo=B MobileNo
如果两张表都没有任何索引 则两张表都要进行全表扫描 所有的数据都要读入内存页
逻辑读数近似等于两张表的数据页总和 SQL Server处理过程中将使用到临时表
只有一张表有聚集索引的情形类似 SQL Server处理过程中将使用到临时表 并且读入所有的数据页到内存
如果两张表都有聚集索引 尽管两表的数据都会被读入内存页 但逻辑读数已经大大减少 等于其中一张表总数据内存页数加上最终输出的数据页数 而且SQL Server处理过程中将不需要再使用临时表
执行高选择性选取
对于这样的高选择性SQL语句 SQL Server 将提示无法生成执行计划
以下是引用片段 Select?*?From?P_Order?A Inner?merge?JOIN?P_User?B?ON?A MobileNo=B MobileNo Where?A MobileNo=
但可以执行
以下是引用片段 Select?*?From?P_Order?A Inner?merge?JOIN?P_User?B?ON?A MobileNo=B MobileNo Where?A MobileNo<= ?(注 最终结果只有 条)
这样的属于低选择性语句 但最终结果也很少的语句 如前面所述 这种情况下 采用netsted loop联接效率可能更高
执行高选择性选取
执行如下的SQL语句
以下是引用片段 Select?*?From?P_Order?A Inner?merge?JOIN?P_User?B?ON?A MobileNo=B MobileNo Where?A MobileNo=
在两个表都没有任何索引情况下 两张表都将执行全表扫描 要读入所有的数据页到内存 总体逻辑读取决于两表的数据页数
在一个表有聚集索引或者非聚集索引情况下 该表将执行Index Seek 另一个表将出现全表扫描 内存数据缓冲区中 将有一张表只读入最终数据所在的数据页 一张表读入全部数据页 逻辑读数取决于表在联接中的秩序 以及无索引表的数据页数
在两个表都有聚集索引情况下 逻辑读最小 每个表只有 到 次 而且只有实际需要输出的数据才会被读入内存页 两个表都有非聚集索引情况下 消耗的逻辑读和内存资源近似
测试 执行Merge Join
执行全表选取或者低选择性选取
执行SQL:
以下是引用片段 Select?*?From?P_Order?A Inner?merge?JOIN?P_User?B?ON?A MobileNo=B MobileNo
如果两张表都没有任何索引 则两张表都要进行全表扫描 所有的数据都要读入内存页
逻辑读数近似等于两张表的数据页总和 SQL Server处理过程中将使用到临时表
只有一张表有聚集索引的情形类似 SQL Server处理过程中将使用到临时表 并且读入所有的数据页到内存
如果两张表都有聚集索引 尽管两表的数据都会被读入内存页 但逻辑读数已经大大减少 等于其中一张表总数据内存页数加上最终输出的数据页数 而且SQL Server处理过程中将不需要再使用临时表
执行高选择性选取
对于这样的高选择性SQL语句 SQL Server 将提示无法生成执行计划
以下是引用片段 Select?*?From?P_Order?A Inner?merge?JOIN?P_User?B?ON?A MobileNo=B MobileNo Where?A MobileNo=
但可以执行
以下是引用片段 Select?*?From?P_Order?A Inner?merge?JOIN?P_User?B?ON?A MobileNo=B MobileNo Where?A MobileNo<= ?(注 最终结果只有 条)
这样的属于低选择性语句 但最终结果也很少的语句 如前面所述 这种情况下 采用netsted loop联接效率可能更高
lishixinzhi/Article/program/SQLServer/201311/22450sql server 单个表中最大多少条记录?
当创建一个新索引,或重建一个存在的索引时,你可以指定一个填充因子,它是在索引创建时索引里的数据页被填充的数量。填充因子设置为100意味着每个索引页100%填满,50%意味着每个索引页50%填满。如果你创建一个填充因子为100的聚集索引(在一个非单调递增的列上),那意味着每当一个记录被插入(或修改)时,页拆分都会发生,因为在现存的页上没有这些数据的空间。很多的页拆分会降低sqlserver的性能。举个例子:假定你刚刚用缺省的填充因子新创建了一个索引。当sqlserver创建它时,它把索引放在相邻的物理页面上,因为数据能够顺序的读所以这样会有最优的i/o访问。但当表随着、、增加和改变时,发生了页拆分。当页拆分发生时,sqlserver必须在磁盘的某处分配一个新的页,这些新的页和最初的物理页不是连续的。因此,访问使用的是随机的i/o,而不是有顺序的i/o,这样访问索引页会变得更慢。那么理想的填充因子是多少呢?它依赖于应用程序对sqlserver表的读和写的比率。首要的原则,按照下面的指导:低更改的表(读写比率为100:1):100%的填充因子高更改的表(写超过读):50-70%的填充因子读写各一半的:80-90%的填充因子在为应用程序找到最优的填充因子前也不得不进行试验。不要假定一个低的填充因子总比高的好。低的填充因子会减少页拆分,它也增加了sqlserver查询期间读的页数量,从而减少性能。太低的填充因子不仅增加i/o开销,也影响缓存。当数据页从磁盘移到缓存中时,整个页(包括空的空间)都移到缓存中。所以填充因子越低,不得不移到sqlserver缓存中的页面就越多,意味着同时为其他重要数据页驻留的空间就少,从而降低性能。如果你没有指定填充因子,缺省的填充因子时0,意味着100%的填充因子(索引的叶页100%的填满,但索引的中间页有预留的空间)。作为监控的一部分,你要决定新建索引或重建索引时的填充因子是多少。事实上,除了只读数据库,所有的情况,缺省值0都是不适合的。
SQL?SERVER中一张表最大记录数是没有限制的,不过会受物理存储空间的限制。解决方法如下:
1、首先在电脑中打开sql server 可视化工具。
2、然后打开sql server新建查询窗口。
3、输出添加语句?insert into jy_name(name,age,remark) values ('姓名',18,'备注')。
4、点击执行按钮,或者按执行快捷键F5。
5、执行成功后,记录添加完成,输入sql查询语句 select * from?jy_name 后查询执行结果。
本文来自作者[兰帝魅晨]投稿,不代表木木号立场,如若转载,请注明出处:https://www.gmx3.cn/ef/%E7%A8%8B%E5%BA%8F%E7%8C%BF%E6%98%AF%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3SQLServer%E5%8D%A0CPU100%25%E7%9A%84.html
评论列表(4条)
我是木木号的签约作者“兰帝魅晨”!
希望本篇文章《程序猿是如何解决SQLServer占CPU100%的》能对你有所帮助!
本站[木木号]内容主要涵盖:生活百科,小常识,生活小窍门,知识分享
本文概览:2.排序(sort) 和 聚合计算(aggregation)在查询的时候,经常会做 order by、distinct 这样的操作,也会做 avg、sum、max、min 这样...