我一直在尝试诊断应用程序中的运行缓慢。为此,我记录了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
计划是完全相同的;它使用相同的参数执行相同的存储过程。