为什么 exec sp_recompile 有时不帮助参数嗅探?

问题描述

我们有一个复杂的存储过程,有时会受到参数嗅探的影响。这是一个大型的“一体化”过程,系统的许多不同部分都会调用它,因此一个查询计划不能适合所有用例是理所当然的。

这很好用,除非定期有一个特定的报告从几秒到几分钟。在过去,快速exec sp_recompile 会立即加快速度。现在那行不通了。该报告最终会在一两天内“自行修复”,这意味着它又回到了几秒钟的时间。

重构存储过程目前不是一个选项,我不想采用其他推荐的方法(将参数保存到局部变量、WITH RECOMPILE、OPTIMIZE FOR UNKNowN),因为据说这些方法有其他副作用。>

所以我有这些问题:

  1. 为什么 exec sp_recompile 不像以前那样加快速度?

  2. 如何判断 exec sp_recompile 是否真的清除了查询计划缓存?在 exec 之前和之后应该运行什么?我尝试了一些网上查询,但无法清楚地判断是否发生了变化,因此最好有一个特定的食谱。

  3. 以不同的名称克隆该过程,并仅针对该报告调用该克隆是否合理?目标是让 sql Server 为报告缓存一个单独的计划。但我不确定 sql Server 是按过程名称缓存计划,还是缓存存储过程中的各种查询。 (如果是后者,则这种方法没有用,因为该过程的任何克隆都将具有相同的查询。)

解决方法

使用多个 CTE,尤其是复杂的查询(就像加入视图一样)可能会导致查询优化器在生成最佳执行计划时出现问题。

如果您使用了大量 CTE 定义,则 SQL Server 将尝试构建一个单一的整体执行计划,并且您可能会遇到计划编译超时,从而导致使用了次优计划。

您可以改为用临时表替换 CTE - 使用中间结果通常具有更好的性能,因为每个查询都使用专用的最佳(或至少更好)计划独立执行。这可以帮助优化器为连接和索引使用做出更好的选择。

如果您可以从理想情况下需要自己的最佳计划的两种关键不同类型的参数中受益,那么您可以选择按照您的建议复制特定于每个用例的过程。

您可以通过使用 dm_exec_sql_text

查询您的过程名称来确认这会产生一个单独的执行计划
select s.plan_handle,t.text
from sys.dm_exec_query_stats s 
cross apply Sys.dm_exec_sql_text(s.plan_handle)t 
where t.text like '%proc name%'

您会注意到每个程序都有不同的plan_handle