SQL Server性能:PREEMPTIVE_OS_DELETESECURITYCONTEXT主导等待类型


8

我昨天接到一个客户的电话,该客户抱怨他们的SQL Server上的CPU使用率过高。我们正在使用SQL Server 2012 64位SE。服务器正在运行Windows Server 2008 R2 Standard,2.20 GHz Intel Xeon(4核),16 GB RAM。

在确定罪魁祸首实际上是SQL Server之后,我在这里使用DMV查询顶部等待实例。前两个等待是:(1)PREEMPTIVE_OS_DELETESECURITYCONTEXT和(2)SOS_SCHEDULER_YIELD

编辑:这是“最等待查询”的结果(尽管有人违反了我的意愿今天早晨重新启动了服务器):

在此处输入图片说明

我们进行了大量的计算/转换,所以我可以理解SOS_SCHEDULER_YIELD。但是,我很好奇PREEMPTIVE_OS_DELETESECURITYCONTEXT等待类型以及为什么它可能是最高的。

最好的说明/讨论,我可以找到这个等待类型,可以发现在这里。它提到:

PREEMPTIVE_OS_等待类型是离开数据库引擎的调用,通常是对Win32 API的调用,它们正在SQL Server外部执行代码以执行各种任务。在这种情况下,它将删除以前用于远程资源访问的安全上下文。相关的API实际上被命名为DeleteSecurityContext()

据我所知,我们没有任何外部资源,例如链接服务器或文件表。而且,我们不进行任何模拟操作,等等。备份是否可能导致备份激增或域控制器出现故障?

到底什么会导致这成为主要的等待类型?如何进一步跟踪此等待类型?

编辑2:我检查了Windows安全日志的内容。我看到了一些可能感兴趣的条目,但不确定这些是否正常:

Special privileges assigned to new logon.

Subject:
    Security ID:        NT SERVICE\MSSQLServerOLAPService
    Account Name:       MSSQLServerOLAPService
    Account Domain:     NT Service
    Logon ID:       0x3143c

Privileges:     SeImpersonatePrivilege

Special privileges assigned to new logon.

Subject:
    Security ID:        NT SERVICE\MSSQLSERVER
    Account Name:       MSSQLSERVER
    Account Domain:     NT Service
    Logon ID:       0x2f872

Privileges:     SeAssignPrimaryTokenPrivilege
            SeImpersonatePrivilege

编辑3:@Jon Seigel,根据您的请求,这是查询的结果。与Paul的稍有不同:

在此处输入图片说明

编辑4:我承认,我是第一次使用扩展事件。我将这种等待类型添加到了wait_info_external事件中,并看到了数百个条目。没有sql文本或计划句柄,只有调用堆栈。如何进一步追踪来源?

在此处输入图片说明


约翰,请您运行sp_whoisactive一段时间(也许一分钟),然后看看弹出了什么?这可能有助于将您/我们引向解决方案。sqlblog.com/files/default.aspx
Mark Wilkinson

约翰,您好,在您的问题中,您提到已将SQL Server确定为罪魁祸首。您能否描述得出该结论所采取的步骤?
Craig Efrein 2014年

Answers:


3

我知道基于标题的这个问题主要与PREEMPTIVE_OS_DELETESECURITYCONTEXT等待类型有关,但是我认为这是对真正问题的误导,即“ 客户抱怨SQL Server上的CPU使用率过高 ”。

我认为专注于这种特定的等待类型是一种疯狂的追求,原因是它在每次建立连接时都会上升。我在笔记本电脑上运行以下查询(意味着我是唯一的用户):

SELECT * 
FROM sys.dm_os_wait_stats
WHERE wait_type = N'PREEMPTIVE_OS_DELETESECURITYCONTEXT'

然后,我执行以下任一操作,然后重新运行此查询:

  • 打开一个新的查询标签
  • 关闭新的查询标签
  • 从DOS提示符下运行以下命令: SQLCMD -E -Q "select 1"

现在,我们知道CPU很高,因此我们应该查看正在运行的进程以查看哪些会话具有较高的CPU:

SELECT req.session_id AS [SPID],
       req.blocking_session_id AS [BlockedBy],
       req.logical_reads AS [LogReads],
       DB_NAME(req.database_id) AS [DatabaseName],
       SUBSTRING(txt.[text],
                 (req.statement_start_offset / 2) + 1,
                 CASE
                     WHEN req.statement_end_offset > 0
                        THEN (req.statement_end_offset - req.statement_start_offset) / 2
                     ELSE LEN(txt.[text])
                 END
                ) AS [CurrentStatement],
       txt.[text] AS [CurrentBatch],
       CONVERT(XML, qplan.query_plan) AS [StatementQueryPlan],
       OBJECT_NAME(qplan.objectid, qplan.[dbid]) AS [ObjectName],
       sess.[program_name],
       sess.[host_name],
       sess.nt_user_name,
       sess.total_scheduled_time,
       sess.memory_usage,
       req.*
FROM sys.dm_exec_requests req
INNER JOIN sys.dm_exec_sessions sess
        ON sess.session_id = req.session_id
CROSS APPLY sys.dm_exec_sql_text(req.[sql_handle]) txt
OUTER APPLY sys.dm_exec_text_query_plan(req.plan_handle,
                                        req.statement_start_offset,
                                        req.statement_end_offset) qplan
WHERE req.session_id <> @@SPID
ORDER BY req.logical_reads DESC, req.cpu_time DESC
--ORDER BY req.cpu_time DESC, req.logical_reads DESC

我通常按​​原样运行上面的查询,但是您也可以切换注释掉了哪个ORDER BY子句,以查看是否给出了更有趣/有用的结果。

或者,您可以基于dm_exec_query_stats运行以下命令以查找成本最高的查询。下面的第一个查询将向您显示单个查询(即使它们有多个计划),并按平均CPU时间排序,但是您可以轻松地将其更改为平均逻辑读取。找到看起来占用大量资源的查询后,将“ sql_handle”和“ statement_start_offset”复制到下面第二个查询的WHERE条件中,以查看各个计划(可以大于1)。滚动到最右侧,并假设有一个XML计划,它将显示为链接(在网格模式下),如果您单击它,它将带您到计划查看器。

查询1:获取查询信息

;WITH cte AS
(
   SELECT qstat.[sql_handle],
          qstat.statement_start_offset,
          qstat.statement_end_offset,
          COUNT(*) AS [NumberOfPlans],
          SUM(qstat.execution_count) AS [TotalExecutions],

          SUM(qstat.total_worker_time) AS [TotalCPU],
          (SUM(qstat.total_worker_time * 1.0) / SUM(qstat.execution_count)) AS [AvgCPUtime],
          MAX(qstat.max_worker_time) AS [MaxCPU],

          SUM(qstat.total_logical_reads) AS [TotalLogicalReads],
   (SUM(qstat.total_logical_reads * 1.0) / SUM(qstat.execution_count)) AS [AvgLogicalReads],
          MAX(qstat.max_logical_reads) AS [MaxLogicalReads],

          SUM(qstat.total_rows) AS [TotalRows],
          (SUM(qstat.total_rows * 1.0) / SUM(qstat.execution_count)) AS [AvgRows],
          MAX(qstat.max_rows) AS [MaxRows]
   FROM sys.dm_exec_query_stats  qstat
   GROUP BY qstat.[sql_handle], qstat.statement_start_offset, qstat.statement_end_offset
)
SELECT  cte.*,
        DB_NAME(txt.[dbid]) AS [DatabaseName],
        SUBSTRING(txt.[text],
                  (cte.statement_start_offset / 2) + 1,
                  CASE
                      WHEN cte.statement_end_offset > 0
                          THEN (cte.statement_end_offset - cte.statement_start_offset) / 2
                      ELSE LEN(txt.[text])
                  END
                 ) AS [CurrentStatement],
        txt.[text] AS [CurrentBatch]
FROM cte
CROSS APPLY sys.dm_exec_sql_text(cte.[sql_handle]) txt
ORDER BY cte.AvgCPUtime DESC

查询2:获取计划信息

SELECT  *,
        DB_NAME(qplan.[dbid]) AS [DatabaseName],
        CONVERT(XML, qplan.query_plan) AS [StatementQueryPlan],
        SUBSTRING(txt.[text],
                  (qstat.statement_start_offset / 2) + 1,
                  CASE
                        WHEN qstat.statement_end_offset > 0
                        THEN (qstat.statement_end_offset - qstat.statement_start_offset) / 2
                        ELSE LEN(txt.[text])
                  END
                 ) AS [CurrentStatement],
        txt.[text] AS [CurrentBatch]
FROM sys.dm_exec_query_stats  qstat
CROSS APPLY sys.dm_exec_sql_text(qstat.[sql_handle]) txt
OUTER APPLY sys.dm_exec_text_query_plan(qstat.plan_handle,
                                        qstat.statement_start_offset,
                                        qstat.statement_end_offset) qplan
-- paste info from Query #1 below
WHERE qstat.[sql_handle] = 0x020000001C70C614D261C85875D4EF3C90BD18D02D62453800....
AND qstat.statement_start_offset = 164
-- paste info from Query #1 above
ORDER BY qstat.total_worker_time DESC

我认为Paul脚本中排名最高的等待类型(即PREEMPTIVE_OS_DELETESECURITYCONTEXT)可能是导致CPU占用过多的原因。在我们的案例中,可以安全地将其视为良性等待类型吗?在我们的应用程序中,我们有一些Windows服务,这些服务不断向SQL Server发送命令(exec存储的proc)。我无法从sys.dm_exec_sessions中识别出太多模式-会话不会保持打开太长时间,并且它们很多。sys.dm_exec_query_stats提供了有关最昂贵的存储proc的一些良好信息,就总体CPU成本而言。这可能是一个不错的起点。
约翰·罗素

我只是想确保我不会错过PREEMPTIVE_OS_DELETESECURITYCONTEXT。我不知道这是否可以追溯到错误的域控制器或AD查找?
约翰·罗素

@JohnRussell:我认为通常需要等待时间最长的类型,但是我的观点是,这种特定类型不仅是由SQL Server中访问外部资源(例如Linked Server或SQLCLR或扩展存储的proc)的代码触发的(例如xp_dirtree),因此高音量并不是真正的指标。即使存在导致延迟的网络延迟,这是否真的会提升CPU或只是增加阻塞?很好,请使用query_stats。稍后我将更新查询。
所罗门·鲁兹基2014年

1
@JohnRussell:关于您的“不断发送命令的Windows服务”,最近有什么变化吗?他们是否正确关闭连接?如果连接时出错,是否正确清理连接?另外,您是否最近重建了索引或至少更新了所有表的统计信息?不这样做可能会导致CPU增加。
所罗门·鲁兹基2014年

感谢您的见解!当我仔细查看sys.dm_exec_query_stats和一些关键表上的索引碎片时,我开始对原因更加有信心。PREEMPTIVE_OS_DELETESECURITYCONTEXT只是让我失望。
John Russell

1

sql服务器在多个地方使用SecurityContext。您命名的一个示例是链接的服务器和文件表。也许您正在使用cmdexec?使用代理帐户的SQL Server代理作业?调用网络服务?远程资源可能是很多有趣的事情。

可以在Windows安全事件中记录模拟事件。可能是您在那里找到了线索。此外,您可能要检查黑盒录像机(又名扩展事件)。

您是否检查过这些等待类型是新的(并与高CPU连接)还是对您的服务器而言只是正常的?


我们没有任何SQL Server代理作业或WebServices。我清除了等待统计信息,然后重新运行了上面的原始查询,并且类似的统计信息重新出现。我花了点时间弄清楚如何重新配置​​system_health扩展事件会话,以将waittype ='PREEMPTIVE_OS_DELETESECURITYCONTEXT'包含为wait_info_external,但最终我添加了它,在观察实时数据的几秒钟内,我可以看到数百个此类事件。我正在研究如何更好地解密源。关于如何追踪此事的任何建议?
约翰·罗素2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.