今天我偶然发现了在兼容级别为80(SQL2000)的数据库中运行在Sql Server 2005 SP2上的存储过程的一个有趣的性能问题。
proc运行大约8分钟,执行计划显示索引的使用情况,实际行数为1.339.241.423,这比表本身的“实际”实际行数(1.144.640)高出约1000倍。如估计的行数正确显示。因此,查询计划优化器给出的实际行数肯定是错误的!
有趣的是,当我将proc中的procs参数值复制到局部变量而不是在实际查询中使用局部变量时,一切正常 - proc运行18秒,执行计划显示正确的实际行数。
编辑:正如TrickyNixon所说,这似乎是参数嗅探问题的一个标志。但事实上,我在两种情况下都完全相同的执行计划。相同的指数以相同的顺序使用。我看到的唯一区别是直接使用参数值时PK_ED_Transitions索引的实际行数很高。
我已经完成了dbcc dbreindex和UPDATE STATISTICS,但没有任何成功。 dbcc show_statistics也显示了索引的良好数据。
使用RECOMPILE创建proc,因此每次运行时都会编译新的执行计划。
更具体一点 - 这个速度很快:
CREATE Proc [dbo].[myProc](
@Param datetime
)
WITH RECOMPILE
as
set nocount on
declare @local datetime
set @local = @Param
select
some columns
from
table1
where
column = @local
group by
some other columns
这个版本运行速度非常慢,但产生完全相同的执行计划(除了使用索引的实际行数太高):
CREATE Proc [dbo].[myProc](
@Param datetime
)
WITH RECOMPILE
as
set nocount on
select
some columns
from
table1
where
column = @Param
group by
some other columns
有什么想法吗? 有谁知道Sql Server在计算查询计划时从哪里获取实际行计数值?
更新:我在另一台服务器上尝试了查询,并将copat模式设置为90(Sql2005)。它的行为相同。我想我会打开ms支持电话,因为这对我来说就像一个bug。
答案 0 :(得分:5)
好的,最后我自己做到了。
两个查询计划的细节不同,我最初错过了。慢速使用嵌套循环运算符将两个子查询连接在一起。这导致索引扫描运算符上当前行计数的高数字,这只是将输入a的行数与输入b的行数相乘的结果。
我仍然不知道为什么优化器决定使用嵌套循环而不是在这种情况下运行1000个计时器的哈希匹配,但我可以通过创建新索引来处理我的问题,以便引擎执行索引寻找statt而不是嵌套循环下的索引扫描。
答案 1 :(得分:2)
当您根据复制/粘贴查询检查存储过程的执行计划时,您使用的是估计计划还是实际计划?确保单击“查询”,“包括执行计划”,然后运行每个查询。比较这些计划,看看有什么不同。
答案 2 :(得分:1)
这听起来像参数嗅探的情况。以下是一个很好的解释以及可能的解决方案:I Smell a Parameter!
这是另一个解决它的StackOverflow线程:Parameter Sniffing (or Spoofing) in SQL Server
答案 3 :(得分:0)
对我来说,听起来好像统计数据不正确。重建索引不一定会更新它们。
您是否已为受影响的表尝试过明确的UPDATE STATISTICS
?
答案 4 :(得分:0)
您是否运行 sp_spaceused 来检查SQL Server是否获得了该表的正确摘要?我相信SQL 2000引擎在构建执行计划时曾经使用过这种元数据。我们以前必须每周运行 DBCC UPDATEUSAGE 来更新一些快速变化的表的元数据,因为SQL Server由于行计数数据不正确而选择了错误的索引。
你正在运行SQL 2005,并且BOL说在2005年你不应该再运行UpdateUsage了,但是因为你处于2000 compat模式,你可能会发现它仍然是必需的。