1

几个星期以来,我一直在与我们服务器上的一个持续问题作斗争。有一个简单的查询可以在几个不同的存储过程中运行,但在其中一些存储过程中,查询可能需要将近 4 分钟才能运行。一些过程具有完全相同的查询,并且它们在 < 1 秒内运行。从查询窗口运行它会在 < 1 秒内返回。

我无法重新创建它 - 我无法运行用户正在运行的过程(因为它会更改数据),并且我们的测试服务器上不会出现问题。我有时可以重新编译(F5)该过程,这似乎重新创建了执行计划,并修复了一段时间,但它总是返回。

查询很简单:

SELECT '1' 
FROM TRANSACTION_LOG WITH (NOLOCK) 
WHERE TYPE = 15 AND SERIAL_NO = @P_TRAVELER_SERIAL
  • TRANSACTION_LOG有 ~90m 行
  • TYPE有一个非聚集索引
  • SERIAL_NO有一个非聚集索引

经过一些研究和分析器捕获后,我在执行计划中发现了一些奇怪的东西。我设法在快速运行时捕获执行计划,在运行缓慢时捕获另一个执行计划。这些来自完全相同的过程,完全相同的查询 - 唯一的区别是@P_TRAVELER_SERIAL参数的值:

快速地:

Compute Scalar(DEFINE:([Expr1005]=CASE WHEN [Expr1006] THEN (1) ELSE (0) END))          
  |--Nested Loops(Left Semi Join, DEFINE:([Expr1006] = [PROBE VALUE]))
    |--Constant Scan
    |--Filter(WHERE:([MICS].[dbo].[Transaction_Log].[TYPE]=(15)))
      |--Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1000], [Expr1010]) WITH UNORDERED PREFETCH)
        |--Index Seek(OBJECT:([MICS].[dbo].[Transaction_Log].[IX_SERIAL_NO]), SEEK:([MICS].[dbo].[Transaction_Log].[SERIAL_NO]=[@P_TRAVELER_SERIAL]) ORDERED FORWARD)
        |--RID Lookup(OBJECT:([MICS].[dbo].[Transaction_Log]), SEEK:([Bmk1000]=[Bmk1000]) LOOKUP ORDERED FORWARD)

慢的:

Compute Scalar(DEFINE:([Expr1005]=CASE WHEN [Expr1006] THEN (1) ELSE (0) END))
  |--Nested Loops(Left Semi Join, DEFINE:([Expr1006] = [PROBE VALUE]))
    |--Constant Scan 
    |--Table Scan(OBJECT:([MICS].[dbo].[Transaction_Log]), WHERE:([MICS].[dbo].[Transaction_Log].[TYPE]=(15) AND [MICS].[dbo].[Transaction_Log].[SERIAL_NO]=[@P_TRAVELER_SERIAL]))

为什么它会在一个计划中使用索引查找,而在另一个计划中使用表扫描?这是相同的查询和相同的程序?表活动与它有什么关系吗?这是一张繁忙的桌子...

谢谢

4

1 回答 1

1

这对我来说听起来像是一个参数嗅探问题。发生的情况是,随着时间的推移,SQL Server 确定您的过程的执行计划已过时,并且下次执行该过程时会编译一个新计划。不幸的是,当这种情况发生时,SQL 服务器在确定为过程生成(和缓存)什么计划时使用当前传入的值。如果当时传入的值不常见,SQL Server 可能会构建并随后缓存执行表扫描的执行计划。虽然这对于传入的不常见值可能是最佳的,但对于将从索引搜索中受益的值显然不是那么理想。

有关详细信息和可能的解决方法,请参阅此 technet 博客文章。 http://blogs.technet.com/b/mdegre/archive/2012/03/19/what-is-parameter-sniffing.aspx

于 2012-09-07T03:43:04.953 回答