这是服务器过载的症状吗?


12

我一直在尝试诊断应用程序中的运行缓慢。为此,我记录了SQL Server 扩展事件

  • 对于这个问题,我正在研究一个特定的存储过程。
  • 但是有一组核心的存储过程,这些存储过程同样可以用作“一对一调查”
  • 而且每当我手动运行其中一个存储过程时,它总是运行很快
  • 如果用户再次尝试:它将运行很快。

存储过程的执行时间千差万别。此存储过程的许多执行都在<1s内返回:

在此处输入图片说明

而对于“快速”存储桶,它远小于1s。实际上大约是90毫秒:

在此处输入图片说明

但是有很多用户需要等待2s,3s,4s秒。有些必须等待12s,13s,14s。然后就是真正的可怜人,他们必须等待22、23、24秒。

30秒后,客户端应用程序放弃,中止查询,用户不得不等待30秒

相关查找因果关系

所以我尝试关联:

  • 持续时间与逻辑读取
  • 持续时间与物理读取
  • 持续时间与CPU时间

而且似乎没有任何关联。似乎没有原因

  • 持续时间与逻辑读取不管是逻辑读取还是多次读取,持续时间仍在剧烈波动

    在此处输入图片说明

  • 持续时间与物理读取即使查询不是从缓存中进行的,并且需要大量物理读取,它也不会影响持续时间:

    在此处输入图片说明

  • duration vs cpu time:查询所用的CPU时间为0s,还是CPU的完整时间为2.5s,则持续时间具有相同的可变性:

    在此处输入图片说明

奖励:我注意到持续时间v物理读取持续时间v CPU时间看起来非常相似。如果我尝试将CPU时间与“物理读取”相关联,就证明了这一点:

在此处输入图片说明

原来很多I / O占用了CPU。谁知道!

因此,如果执行查询的行为没有什么可以解释执行时间的差异,这是否暗示它与CPU或硬盘驱动器无关?

如果CPU或硬盘驱动器是瓶颈;难道不是瓶颈吗?

如果我们假设是CPU是瓶颈,那么,该服务器的CPU电源不足:

  • 那么执行使用更多CPU时间的时间不会更长吗?
  • 因为他们必须使用过载的CPU与他人一起完成?

对于硬盘驱动器类似。如果我们假设硬盘驱动器是瓶颈;硬盘驱动器对此服务器没有足够的随机吞吐量:

  • 那么执行更多物理读取操作不会花费更长的时间吗?
  • 因为他们必须使用过载的硬盘I / O与其他人一起完成?

存储过程本身既不执行也不要求任何写操作。

  • 通常它返回0行(90%)。
  • 有时它将返回1行(7%)。
  • 很少会返回2行(1.4%)。
  • 在最坏的情况下,它已返回2行以上(一次返回12行)

因此,这并不像在返回疯狂的数据量。

服务器CPU使用率

服务器的处理器使用率平均约为1.8%,偶尔会激增至18%-因此看来CPU负载似乎不是问题:

在此处输入图片说明

因此,服务器CPU似乎没有过载。

但是服务器虚拟的...

宇宙之外的东西?

我能想象的唯一剩下的就是服务器领域之外的东西。

  • 如果不是逻辑读取
  • 这不是物理读
  • 这不是CPU的使用
  • 而且不是CPU负载

而且,它不像是存储过程的参数(因为手动发出相同的查询,并且不需要27秒-大约需要0秒)。

其他原因可能导致服务器有时需要30秒而不是0秒来运行相同的已编译存储过程。

  • 检查站?

这是一个虚拟服务器

  • 主机超载?
  • 同一主机上的另一个VM?

经历服务器的扩展事件;当查询突然花费20秒时,没有其他事情发生。运行正常,然后决定运行不正常:

  • 2秒
  • 1秒
  • 30秒
  • 3秒
  • 2秒

我找不到其他特别费力的物品。并非每隔2小时进行一次交易日志备份。

还有什么是什么?

除了“服务器”之外,我还能说什么吗?

编辑:按一天中的时间进行关联

我意识到我已经将持续时间与所有内容相关联:

  • 逻辑读
  • 物理阅读
  • CPU使用率

但是我与之无关的一件事是一天中的时间。每隔2小时的事务日志备份可能一个问题。

还是放慢速度,确实在检查站发生卡盘?

不:

在此处输入图片说明

英特尔至强金四核6142。

编辑-人们正在假想查询执行计划

人们假设查询执行计划在“快速”和“慢速”之间必须有所不同。他们不是。

我们可以从检查中立即看到这一点。

我们知道更长的问题持续时间并不是因为执行计划较差:

  • 一个需要更多逻辑读取的
  • 通过更多的联接和键查找消耗了更多CPU的处理器

因为如果读取次数的增加或CPU的增加是导致查询持续时间增加的原因,那么我们已经在上面看到了。没有相关性。

但是,让我们尝试将持续时间与CPU读取区域产品指标相关联:

在此处输入图片说明

相互关系变得越来越少-这是一个悖论。


编辑:更新了散点图,以解决具有大量值的Excel散点图中的错误。

下一步

我的下一步是让某人必须在服务器上为被阻止的查询生成事件-5秒后:

EXEC sp_configure 'blocked process threshold', '5';
RECONFIGURE

它不会解释查询是否被阻止4秒钟。但是,可能阻塞查询5秒钟的所有内容也会阻塞4秒。

慢计划

这是正在执行的两个存储过程的慢速计划:

  • `执行FindFrob @CustomerID = 7383,@StartDate ='20190725 04:00:00.000',@EndDate ='20190726 04:00:00.000'
  • `执行FindFrob @CustomerID = 7383,@StartDate ='20190725 04:00:00.000',@EndDate ='20190726 04:00:00.000'

相同的存储过程和相同的参数将连续运行:

| Duration (us) | CPU time (us) | Logical reads | Physical reads | 
|---------------|---------------|---------------|----------------|
|    13,984,446 |        47,000 |         5,110 |            771 |
|     4,603,566 |        47,000 |         5,126 |            740 |

通话1:

|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
    |    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]),  WHERE:([Contos
    |--Filter(WHERE:([Expr1009]>(1)))
     |--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
          |--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
           |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC

通话2

|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
    |    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]),  WHERE:([Contos
    |--Filter(WHERE:([Expr1009]>(1)))
     |--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
          |--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
           |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC

计划是完全相同的;它使用相同的参数执行相同的存储过程。


2
您可以发布查询计划-好运还是坏运?
Kin Shah

4
我从这里的第一个猜测就是封锁...
Tibor Karaszi

3
谁对此表示反对?即使缺少查询计划,这也是一个非常详细,经过深入研究的问题!向我+1!
Vérace

4
您如何得出查询计划“相同”的结论?您是说它们形状相同吗?将它们张贴在某个地方,以便我们也可以进行比较。告诉我们它们是相同的并不意味着它们是相同的。
亚伦·伯特兰

3
使用PasteThePlan添加自动执行计划可以使我们了解查询正在等待什么。
兰迪·沃顿根

Answers:


2

看一下wait_stats,它将显示SQL服务器上最大的瓶颈。

我最近遇到了一个外部应用程序间歇性缓慢的问题。但是,在服务器本身上运行存储过程总是非常快。

性能监控显示,SQL缓存或服务器上的RAM使用率和IO完全没有关系。

帮助缩小调查范围的是查询SQL在以下位置收集的等待状态 sys.dm_os_wait_stats

SQLSkills网站上的出色脚本将向您展示您遇到最多的脚本。然后,您可以缩小搜索范围以找出原因。

一旦知道了等待是什么大问题,此脚本将有助于缩小正在等待的会话/数据库的范围:

SELECT OSW.session_id,
       OSW.wait_duration_ms,
       OSW.wait_type,
       DB_NAME(EXR.database_id) AS DatabaseName
FROM sys.dm_os_waiting_tasks OSW
INNER JOIN sys.dm_exec_sessions EXS ON OSW.session_id = EXS.session_id
INNER JOIN sys.dm_exec_requests EXR ON EXR.session_id = OSW.session_id
OPTION(Recompile);

以上查询和更多详细信息来自MSSQLTips网站

sp_BlitzFirst布伦特·奥扎(Brent Ozar)网站上的脚本还将向您显示导致速度下降的原因。

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.