为什么 exec sp_recompile 有时不帮助参数嗅探?
Why doesn't exec sp_recompile sometimes not help parameter sniffing?
我们有一个复杂的存储过程,有时会受到参数嗅探的影响。它是一个大型的“一体式”过程,由系统的许多不同部分调用,因此一个查询计划不适合所有用例是理所当然的。
这工作正常,除了一个特定的报告周期性地从几秒到几分钟。过去,快速 exec sp_recompile
会立即加快速度。现在那永远行不通了。该报告最终会在一两天内“自行修复”,这意味着它需要几秒钟的时间。
重构存储过程目前不是一个选项,我不想执行其他推荐的方法(将参数保存到局部变量、重新编译、针对未知进行优化),因为据说这些方法有其他副作用。
所以我有这些问题:
为什么 exec sp_recompile
不能像以前那样加快速度?
如何判断 exec sp_recompile
是否真的清除了查询计划缓存? exec 之前和之后的 运行 应该是什么?我尝试了一些来自网络的查询,但无法清楚地判断是否发生了变化,所以最好有一个具体的食谱。
用不同的名称克隆过程,并只为这份报告调用该克隆是否合理?目标是让 SQL 服务器为报告缓存一个单独的计划。但我不确定 SQL 服务器是否按过程名称缓存计划,或者它是否在存储过程中缓存各种查询。 (如果是后者,那么这种方法就没有用了,因为该过程的任何克隆都会有相同的查询。)
使用多个 CTE,尤其是复杂查询(就像连接视图时一样)可能会导致查询优化器在生成最佳执行计划时出现问题。
如果您使用了很多 CTE 定义,SQL 服务器将尝试构建一个单一的整体执行计划,您可能会遇到计划编译超时,从而导致使用次优计划。
您可以将 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。
我们有一个复杂的存储过程,有时会受到参数嗅探的影响。它是一个大型的“一体式”过程,由系统的许多不同部分调用,因此一个查询计划不适合所有用例是理所当然的。
这工作正常,除了一个特定的报告周期性地从几秒到几分钟。过去,快速 exec sp_recompile
会立即加快速度。现在那永远行不通了。该报告最终会在一两天内“自行修复”,这意味着它需要几秒钟的时间。
重构存储过程目前不是一个选项,我不想执行其他推荐的方法(将参数保存到局部变量、重新编译、针对未知进行优化),因为据说这些方法有其他副作用。
所以我有这些问题:
为什么
exec sp_recompile
不能像以前那样加快速度?如何判断
exec sp_recompile
是否真的清除了查询计划缓存? exec 之前和之后的 运行 应该是什么?我尝试了一些来自网络的查询,但无法清楚地判断是否发生了变化,所以最好有一个具体的食谱。用不同的名称克隆过程,并只为这份报告调用该克隆是否合理?目标是让 SQL 服务器为报告缓存一个单独的计划。但我不确定 SQL 服务器是否按过程名称缓存计划,或者它是否在存储过程中缓存各种查询。 (如果是后者,那么这种方法就没有用了,因为该过程的任何克隆都会有相同的查询。)
使用多个 CTE,尤其是复杂查询(就像连接视图时一样)可能会导致查询优化器在生成最佳执行计划时出现问题。
如果您使用了很多 CTE 定义,SQL 服务器将尝试构建一个单一的整体执行计划,您可能会遇到计划编译超时,从而导致使用次优计划。
您可以将 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。