本文提供了诊断和修复运行 Microsoft SQL Server 的计算机上 CPU 使用率过高导致的问题的过程。 尽管在 SQL Server 中出现 CPU 使用率过高有许多可能原因,但以下原因最为常见:

  • List item

    • 由于以下情况,表或索引扫描导致的高逻辑读取:
    • 过期统计信息
    • 缺少索引
    • 参数敏感计划 (PSP) 问题
    • 设计不佳的查询
  • 工作负荷增加
    可以使用以下步骤来解决 SQL Server 中 CPU 使用率过高的问题。

步骤 1:验证 SQL Server 是否导致 CPU 使用率过高

使用以下工具之一检查 SQL Server 进程是否确实导致 CPU 使用率过高:

  • 任务管理器:在“进程”选项卡上,检查“64 位版本的 SQL Server Windows NT”的“CPU”列的值是否接近 100%。
  • 性能和资源监视器 (perfmon)
    • 计数器:Process/%User Time, % Privileged Time
    • 实例:sqlservr
  • 可以使用以下 PowerShell 脚本在 60 秒的跨度内收集计数器数据:
$serverName = $env:COMPUTERNAME
$Counters = @(("\\$serverName" + "\Process(sqlservr*)\% User Time"), ("\\$serverName" + "\Process(sqlservr*)\% Privileged Time")
)
Get-Counter -Counter $Counters -MaxSamples 30 | ForEach {$_.CounterSamples | ForEach {[pscustomobject]@{TimeStamp = $_.TimeStampPath = $_.PathValue = ([Math]::Round($_.CookedValue, 3))}Start-Sleep -s 2}
}

如果 % User Time 始终大于 90%,则是 SQL Server 进程导致 CPU 使用率过高。 但是,如果 % Privileged time 始终大于 90%,则是防病毒软件、其他驱动程序或计算机上的其他操作系统组件导致 CPU 使用率过高。 应与系统管理员合作,分析此行为的根本原因。

步骤 2:确定影响 CPU 使用率的查询

如果 Sqlservr.exe 进程导致 CPU 使用率过高,则最常见的原因是执行表或索引扫描的 SQL Server 查询,其次是排序、哈希操作和循环 (嵌套循环运算符或 WHILE (T-SQL) ) 。 要了解查询当前在总 CPU 使用率中的占比,请运行以下语句:

DECLARE @init_sum_cpu_time int,@utilizedCpuCount int
--get CPU count used by SQL Server
SELECT @utilizedCpuCount = COUNT( * )
FROM sys.dm_os_schedulers
WHERE status = 'VISIBLE ONLINE'
--calculate the CPU usage by queries OVER a 5 sec interval
SELECT @init_sum_cpu_time = SUM(cpu_time)
FROM sys.dm_exec_requests WAITFOR DELAY '00:00:05'SELECT CONVERT(DECIMAL(5,2),((SUM(cpu_time) - @init_sum_cpu_time) / (@utilizedCpuCount * 5000.00)) * 100) AS [CPU FROM Queries AS Percent of Total CPU Capacity]
FROM sys.dm_exec_requests

若要确定当前负责高 CPU 活动的查询,请运行以下语句:

SELECT TOP 10 s.session_id,r.status,r.cpu_time,r.logical_reads,r.reads,r.writes,r.total_elapsed_time / (1000 * 60) 'Elaps M',SUBSTRING(st.TEXT, (r.statement_start_offset / 2) + 1,((CASE r.statement_end_offsetWHEN -1 THEN DATALENGTH(st.TEXT)ELSE r.statement_end_offsetEND - r.statement_start_offset) / 2) + 1) AS statement_text,COALESCE(QUOTENAME(DB_NAME(st.dbid)) + N'.' + QUOTENAME(OBJECT_SCHEMA_NAME(st.objectid, st.dbid)) + N'.' + QUOTENAME(OBJECT_NAME(st.objectid, st.dbid)), '') AS command_text,r.command,s.login_name,s.host_name,s.program_name,s.last_request_end_time,s.login_time,r.open_transaction_count
FROM sys.dm_exec_sessions AS s
JOIN sys.dm_exec_requests AS r ON r.session_id = s.session_id CROSS APPLY sys.Dm_exec_sql_text(r.sql_handle) AS st
WHERE r.session_id != @@SPID
ORDER BY r.cpu_time DESC

如果查询目前未驱动 CPU,可以运行以下语句来查找历史占用大量 CPU 的查询:

SELECT TOP 10 st.text AS batch_text,SUBSTRING(st.TEXT, (qs.statement_start_offset / 2) + 1, ((CASE qs.statement_end_offset WHEN - 1 THEN DATALENGTH(st.TEXT) ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2) + 1) AS statement_text,(qs.total_worker_time / 1000) / qs.execution_count AS avg_cpu_time_ms,(qs.total_elapsed_time / 1000) / qs.execution_count AS avg_elapsed_time_ms,qs.total_logical_reads / qs.execution_count AS avg_logical_reads,(qs.total_worker_time / 1000) AS cumulative_cpu_time_all_executions_ms,(qs.total_elapsed_time / 1000) AS cumulative_elapsed_time_all_executions_ms
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(sql_handle) st
ORDER BY(qs.total_worker_time / qs.execution_count) DESC

步骤 3:更新统计信息

在确定 CPU 占用最高的查询后,请更新这些查询使用的表的“更新统计信息” 。 可以使用 sp_updatestats 系统存储过程更新当前数据库中所有用户定义表和内部表的统计信息。 例如:

exec sp_updatestats

备注
sp_updatestats系统存储过程针对当前数据库中的所有用户定义表和内部表运行UPDATE STATISTICS。 对于定期维护,请确保定期计划维护使统计信息保持最新。 使用“自适应索引碎片整理”等解决方案自动管理一个或多个数据库的索引碎片整理和统计信息更新。 此过程自动选择是根据索引的碎片级别和其他参数重新生成还是重新组织索引,并使用线性阈值更新统计信息。

如果 SQL Server 的 CPU 占用仍然过高,请前往下一步。

步骤 4:添加缺失索引

缺少索引可能导致运行速度较慢的查询和 CPU 使用率过高。 可以识别缺失的索引并创建这些索引,以改善这种性能影响。

  1. 运行以下查询以识别导致 CPU 使用率高且在查询计划中至少包含一个缺失索引的查询:
-- Captures the Total CPU time spent by a query along with the query plan and total executions
SELECTqs_cpu.total_worker_time / 1000 AS total_cpu_time_ms,q.[text],p.query_plan,qs_cpu.execution_count,q.dbid,q.objectid,q.encrypted AS text_encrypted
FROM(SELECT TOP 500 qs.plan_handle,qs.total_worker_time,qs.execution_count FROM sys.dm_exec_query_stats qs ORDER BY qs.total_worker_time DESC) AS qs_cpu
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS q
CROSS APPLY sys.dm_exec_query_plan(plan_handle) p
WHERE p.query_plan.exist('declare namespace qplan = "http://schemas.microsoft.com/sqlserver/2004/07/showplan";//qplan:MissingIndexes')=1
  1. 查看已标识查询的执行计划,并通过进行所需的更改来优化查询。 以下屏幕截图显示了一个示例,其中 SQL Server 将指出查询的缺失索引。 右键单击查询计划的“缺失索引”部分,然后选择“缺少索引详细信息”,在 SQL Server Management Studio 的另一个窗口中创建索引。
  2. 使用以下查询检查是否缺少索引,并应用具有高改进度量值的任何建议索引。 从输出中具有最高 improvement_measure 值的前 5 或 10 条建议开始。 这些索引对性能有最显著的积极影响。 确定是否要应用这些索引,并确保对应用程序进行了性能测试。 然后,继续应用缺失索引建议,直到获得所需的应用程序性能结果。
SELECT CONVERT(VARCHAR(30), GETDATE(), 126) AS runtime,mig.index_group_handle,mid.index_handle,CONVERT(DECIMAL(28, 1), migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans)) AS improvement_measure,'CREATE INDEX missing_index_' + CONVERT(VARCHAR, mig.index_group_handle) + '_' + CONVERT(VARCHAR, mid.index_handle) + ' ON ' + mid.statement + ' (' + ISNULL(mid.equality_columns,'') + CASE WHEN mid.equality_columns IS NOT NULL
AND mid.inequality_columns IS NOT NULL THEN ','
ELSE ''
END + ISNULL(mid.inequality_columns,'') + ')' + ISNULL(' INCLUDE (' + mid.included_columns + ')','') AS create_index_statement,migs.*,mid.database_id,mid.[object_id]
FROM sys.dm_db_missing_index_groups mig
INNER JOIN sys.dm_db_missing_index_group_stats migs ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle
WHERE CONVERT (DECIMAL (28, 1),migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans)) > 10
ORDER BY migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans) DESC

步骤 5:调查并解决参数敏感问题

可以使用 DBCC FREEPROCCACHE 命令释放计划缓存,并检查这是否解决了 CPU 使用率过高的问题。 如果问题已修复,则表示是参数敏感问题(PSP,也称为“参数探查问题”)。

备注
使用不带参数的 DBCC FREEPROCCACHE 将从计划缓存中删除所有已编译的计划。 这将导致再次编译新的查询执行,从而导致每个新查询的持续时间一次性延长。 最佳方法是使用 DBCC FREEPROCCACHE ( plan_handle | sql_handle ) 来识别导致问题的查询,然后解决单个查询或有问题的查询。

要解决此参数敏感问题,请使用以下方法。 每种方法都有相应的利弊。

  • 使用 RECOMPILE 查询提示。 可以向步骤 2 中标识的一个或多个 CPU 过高查询添加 RECOMPILE 查询提示。 此提示将使编译 CPU 使用率略微增加,有助于在与每个查询执行的更佳性能之间达到平衡。 有关详细信息,请参阅参数和执行计划重用、 参数敏感度和 RECOMPILE 查询提示。

    下面是如何将此提示应用到查询的示例。

    SELECT * FROM Person.Person
    WHERE LastName = 'Wood'
    OPTION (RECOMPILE)
    
  • 使用 OPTIMIZE FOR 查询提示,使用包含数据中大多数值的更典型的参数值覆盖实际参数值。 此选项需要充分了解最佳参数值和关联的计划特征。 下面是如何在查询中使用此提示的示例。

    DECLARE @LastName Name = 'Frintu'
    SELECT FirstName, LastName FROM Person.Person
    WHERE LastName = @LastName
    OPTION (OPTIMIZE FOR (@LastName = 'Wood'))
    
  • 使用优化未知查询提示,使用密度向量平均值覆盖实际参数值。 也可以通过捕获本地变量中的传入参数值,然后使用谓词中的本地变量而不是使用参数本身来执行此操作。 对于此修复,平均密度可能足以提供可接受的性能。

  • 使用 DISABLE_PARAMETER_SNIFFING 查询提示,完全禁用参数探查。 下面是如何在查询中使用它的示例:

    SELECT * FROM Person.Address
    WHERE City = 'SEATTLE' AND PostalCode = 98104
    OPTION (USE HINT ('DISABLE_PARAMETER_SNIFFING'))
    
  • 使用 KEEPFIXED PLAN 查询提示,阻止在缓存中重新编译。 此解决方法假定“足够好”的常见计划是已在缓存中的计划。 还可以禁用自动统计信息更新,以减少将好计划逐出并编译新的错误计划的可能性。

  • 将 DBCC FREEPROCCACHE 命令用作临时解决方案,直到应用程序代码修复为止。 可以使用 DBCC FREEPROCCACHE (plan_handle) 命令仅删除导致问题的计划。 例如,若要查找引用 AdventureWorks 中 Person.Person 表的查询计划,可以使用此查询查找查询句柄。 然后,可以使用查询结果第二列中生成的 DBCC FREEPROCCACHE (plan_handle),从缓存中释放特定查询计划。

    SELECT text, 'DBCC FREEPROCCACHE (0x' + CONVERT(VARCHAR (512), plan_handle, 2) + ')' AS dbcc_freeproc_command FROM sys.dm_exec_cached_plans
    CROSS APPLY sys.dm_exec_query_plan(plan_handle)
    CROSS APPLY sys.dm_exec_sql_text(plan_handle)
    WHERE text LIKE '%person.person%'
    

步骤 6:调查并解决 SARGability 问题

当 SQL Server 引擎可以使用索引查找来加快查询的执行速度时,查询中的谓词将被视为 SARGable (Search ARGument-able)。 许多查询设计会阻止 SARGability,并导致表或索引扫描和 CPU 使用率过高。 请考虑 AdventureWorks 数据库的以下查询,其中必须检索每个 ProductNumber 并向其应用 SUBSTRING() 函数,然后再将其与字符串文本值进行比较。 如你所见,必须先提取表的所有行,然后应用函数,然后才能进行比较。 从表中提取所有行意味着表或索引扫描,这会导致 CPU 使用率较高。

SELECT ProductID, Name, ProductNumber
FROM [Production].[Product]
WHERE SUBSTRING(ProductNumber, 0, 4) =  'HN-'

对搜索谓词中的列应用任何函数或计算通常会使查询非 sargable,并导致 CPU 消耗较高。 解决方案通常涉及以创造性的方式重写查询,使其 SARGable。 此示例的一个可能解决方案是重写,其中从查询谓词中删除函数,搜索另一列,并获得相同的结果:

SELECT ProductID, Name, ProductNumber
FROM [Production].[Product]
WHERE Name LIKE  'Hex%'

下面是另一个示例,销售经理可能希望为大额订单提供 10% 的销售佣金,并希望查看哪些订单的佣金将超过 300 美元。 这是合乎逻辑的,但非 sargable 的方法。

SELECT DISTINCT SalesOrderID, UnitPrice, UnitPrice * 0.10 [10% Commission]
FROM [Sales].[SalesOrderDetail]
WHERE UnitPrice * 0.10 > 300

下面是一个可能不太直观,但以 SARGable 重写查询的方法,其中计算将移动到谓词的另一边。

SELECT DISTINCT SalesOrderID, UnitPrice, UnitPrice * 0.10 [10% Commission]
FROM [Sales].[SalesOrderDetail]
WHERE UnitPrice > 300/0.10

SARGability 不仅适用于 WHERE 子句,也适用于 JOINsHAVINGGROUP BYORDER BY 子句。 查询中频繁出现的阻止 SARGability 涉及导致列扫描的子句 WHEREJOIN 中使用的 CONVERT()CAST()ISNULL()COALESCE() 函数。 在数据类型转换情况下(CONVERTCAST),解决方案可能会确保你正在比较相同的数据类型。 下面是一个示例,其中 T1.ProdID 列显式转换为 JOIN 中的 INT 数据类型。 此转换会阻止在联接列上使用索引。 当数据类型不同且 SQL Server 转换其中一种类型以执行联接的隐式转换时,也会出现相同的问题。

SELECT T1.ProdID, T1.ProdDesc
FROM T1 JOIN T2
ON CONVERT(int, T1.ProdID) = T2.ProductID
WHERE t2.ProductID BETWEEN 200 AND 300

为了避免对表进行 T1 扫描,可以在正确规划和设计后更改 ProdID 列的基础数据类型,然后联接这两列,而无需使用转换函数 ON T1.ProdID = T2.ProductID

另一种解决方案是在 T1 中创建一个使用相同 CONVERT() 函数的计算列,然后在其上创建索引。 这将允许查询优化器使用该索引,而无需更改查询。

ALTER TABLE dbo.T1  ADD IntProdID AS CONVERT (INT, ProdID);
CREATE INDEX IndProdID_int ON dbo.T1 (IntProdID);

步骤 7:禁用重度跟踪

检查 SQL 跟踪或 XEvent 跟踪,这些跟踪会影响 SQL Server 性能并导致 CPU 使用率过高。 例如,如果跟踪重度 SQL Server 活动,使用以下事件可能会导致 CPU 使用率过高:

  • 查询计划 XML 事件 (query_plan_profile, query_post_compilation_showplan, query_post_execution_plan_profile, query_post_execution_showplan, query_pre_execution_showplan)
  • 语句级事件 (sql_statement_completed, sql_statement_starting, sp_statement_starting, sp_statement_completed)
  • 登录和注销事件 (login, process_login_finish, login_event, logout)
  • 锁定事件 (lock_acquired, lock_cancel, lock_released)
  • 等待事件 (wait_info, wait_info_external)
  • SQL 审核事件(取决于该组中审核的组和 SQL Server 活动)

运行以下查询以确定活动的 XEvent 或 Server 跟踪:

PRINT '--Profiler trace summary--'
SELECT traceid, property, CONVERT(VARCHAR(1024), value) AS value FROM::fn_trace_getinfo(default)
GO
PRINT '--Trace event details--'
SELECT trace_id,status,CASE WHEN row_number = 1 THEN path ELSE NULL end AS path,CASE WHEN row_number = 1 THEN max_size ELSE NULL end AS max_size,CASE WHEN row_number = 1 THEN start_time ELSE NULL end AS start_time,CASE WHEN row_number = 1 THEN stop_time ELSE NULL end AS stop_time,max_files,is_rowset,is_rollover,is_shutdown,is_default,buffer_count,buffer_size,last_event_time,event_count,trace_event_id,trace_event_name,trace_column_id,trace_column_name,expensive_event
FROM(SELECT t.id AS trace_id,row_number() over(PARTITION BY t.id order by te.trace_event_id, tc.trace_column_id) AS row_number,t.status,t.path,t.max_size,t.start_time,t.stop_time,t.max_files,t.is_rowset,t.is_rollover,t.is_shutdown,t.is_default,t.buffer_count,t.buffer_size,t.last_event_time,t.event_count,te.trace_event_id,te.name AS trace_event_name,tc.trace_column_id,tc.name AS trace_column_name,CASE WHEN te.trace_event_id in (23, 24, 40, 41, 44, 45, 51, 52, 54, 68, 96, 97, 98, 113, 114, 122, 146, 180) THEN CAST(1 as bit) ELSE CAST(0 AS BIT) END AS expensive_event FROM sys.traces t CROSS APPLY::fn_trace_geteventinfo(t.id) AS e JOIN sys.trace_events te ON te.trace_event_id = e.eventid JOIN sys.trace_columns tc ON e.columnid = trace_column_id) AS x
GO
PRINT '--XEvent Session Details--'
SELECT sess.NAME 'session_name', event_name, xe_event_name, trace_event_id,CASE WHEN xemap.trace_event_id IN(23, 24, 40, 41, 44, 45, 51, 52, 54, 68, 96, 97, 98, 113, 114, 122, 146, 180) THEN Cast(1 AS BIT)
ELSE Cast(0 AS BIT)
END AS expensive_event
FROM sys.dm_xe_sessions sess
JOIN sys.dm_xe_session_events evt
ON sess.address = evt.event_session_address
INNER JOIN sys.trace_xe_event_map xemap
ON evt.event_name = xemap.xe_event_name
GO

解决 SQL Server 中 CPU 使用率过高的问题相关推荐

  1. sql server死锁_如何解决SQL Server中的死锁

    sql server死锁 In this article, we will talk about the deadlocks in SQL Server, and then we will analy ...

  2. sql 闩锁 原因_如何识别和解决SQL Server中的热闩锁

    sql 闩锁 原因 描述 (Description) In SQL Server, internal latch architecture protects memory during SQL ope ...

  3. 如何解决SQL Server中SQL身份危机

    描述 (Description) SQL Identity columns provide a convenient way to auto-number an ID column within a ...

  4. ubuntu+nvidia显卡驱动安装+(顺带)解决rviz卡顿/CPU使用率过高问题/compiz占用CPU过高问题

    前言 我原本安装nvidia显卡驱动的方式为:从官网下载*.run文件,参考教程link.虽然安装成功,运行深度学习相关代码没有出状况,但仍然存在一些问题.重新通过ppa方式安装后,Rviz/pcl_ ...

  5. 解决SQL Server占用服务器内存过高问题

    最近发现个问题,数据库服务器内存居高不下,64G的内存,几乎被占用100%,结果差点把服务器给拖垮了. 第一步.打开SQL Server Management Studio,在连接上右键 → 属性 第 ...

  6. linux杀掉cpu使用率高的进程,如何解决Linux中CPU使用率高的问题?

    电脑死机了?还是开始听到电脑发出很响的CPU风扇噪音?Linux中CPU使用率过高的原因很多,但最常见的原因是运行异常的应用程序.本文介绍了如何在Linux中解决CPU使用率过高的问题. 找出罪魁祸首 ...

  7. sql查询非ascii字符_SQL替换:如何在SQL Server中替换ASCII特殊字符

    sql查询非ascii字符 One of the important steps in an ETL process involves the transformation of source dat ...

  8. 什么是SQL Server中的数据库规范化?

    In addition to specifically addressing database normalization in SQL Server, this article will also ...

  9. 如何在.NET应用程序中分析CPU使用率过高的问题

    原文来自互联网,由长沙DotNET技术社区编译.如译文侵犯您的署名权或版权,请联系小编,小编将在24小时内删除.限于译者的能力有限,个别语句翻译略显生硬,还请见谅. 作者:胡安·帕勃罗·希达,JUAN ...

最新文章

  1. 数据库 user schema sqlserver 关系
  2. SQL中的left join与right join
  3. 【Linux】一步一步学Linux——watch命令(135)
  4. java数据类型_JAVA基础篇(数据类型)
  5. Unity 4 3 制作一个2D横版射击游戏
  6. manjaro 搜狗输入法_Manjaro日常使用 之一:日常办公
  7. linux安装typecho教程,Typecho上手指南
  8. Unity Lighting(一)光照练习
  9. 刘德华--6雪藏是一种代价
  10. Atitit 面向对象弊端与问题 坏处 缺点
  11. 撰写美国作业使用APA与MLA格式的区别是什么?
  12. NYOJ234吃土豆(双层动态规划)
  13. 用python画年度和月度的日历图
  14. C++ int、long、long int、long long、uint64_t字节长度
  15. 【织梦总结】织梦常用调用标签总结
  16. asp.net 著名网站
  17. 拓嘉辰丰:拼多多详情图制作方法
  18. html中可编辑的表格控件,Editable DataGrid(可编辑表格)
  19. 百度快照更新是什么意思?
  20. c语言ftest函数,阅读以下说明、c函数和问题,将解答写入答题纸的对应栏内。【说明1】函数testf - 信管网...

热门文章

  1. linux关闭established状态,有关ESTABLISHED,CLOSE_WAIT,TIME_WAIT等连接状态
  2. linux7 epel源,为CentOS7添加EPEL源
  3. 【情报百科】如何根据车牌追踪汽车信息
  4. Matlab绘图工具箱Gramm
  5. Oracle 11g 精简客户端
  6. 乖离性暗机器人_乖离性百万亚瑟王超弩级暗机器人攻略 黑暗切尔莉打法
  7. windows批处理:if else的踩坑点及排版优化
  8. Python基础——1.变量和简单数据类型
  9. python爬新闻并保存_利用python的scrapy爬取新浪新闻保存至txt
  10. 如何解决打开U盘时遇到提示:请将磁盘插入驱动器