SQL Server - 参数嗅探

use*_*183 13 sql-server parameter-sniffing

我已经阅读了很多关于参数嗅探的文章,但目前尚不清楚这是好还是坏.任何人都可以通过一个简单的例子来解释

有没有办法自动检测错误的计划分配给特定的声明?

提前致谢.

Mik*_*son 20

它很好但有时可能很糟糕.

参数嗅探是关于查询优化器使用提供的参数的值来确定可能的最佳查询计划.许多选择中的一个和非常容易理解的选择之一是,应该扫描整个表以获取值,还是使用索引搜索更快.如果参数中的值具有高度选择性,优化器可能会使用seek构建查询计划,如果不是,查询将对您的表进行扫描.

然后缓存查询计划并重复使用具有不同值的连续查询.参数嗅探的不良部分是缓存计划不是其中一个值的最佳选择.

样本数据:

create table T
(
  ID int identity primary key,
  Value int not null,
  AnotherValue int null
);

create index IX_T_Value on T(Value);

insert into T(Value) values(1);

insert into T(Value)
select 2
from sys.all_objects;
Run Code Online (Sandbox Code Playgroud)

T是一个包含几千行的表,其值为非聚集索引.有一行是值1,其余的是值2.

示例查询:

select *
from T 
where Value = @Value;
Run Code Online (Sandbox Code Playgroud)

查询优化器在此处的选择是执行聚簇索引扫描并针对每一行检查where子句,或使用索引查找来查找匹配的行,然后执行密钥查找以从要求的列中获取值列列表.

当嗅探值是1查询计划时,它将如下所示:

在此输入图像描述

当嗅觉值是2这样时,它将如下所示:

在此输入图像描述

在这种情况下参数嗅探的不良部分发生在构建查询计划时嗅探a 1但稍后执行的值为2.

在此输入图像描述

您可以看到Key Lookup执行了2352次.扫描显然是更好的选择.

总结一下,我会说参数嗅探是一件好事,你应该尝试通过使用查询参数尽可能地发生.有时它可能会出错,在这种情况下,很可能是由于数据偏差导致您的统计信息混乱.

更新:

这是针对几个dmv的查询,您可以使用它来查找系统上最昂贵的查询.更改为order by子句,以便根据您的要求使用不同的标准.我认为这TotalDuration是一个很好的起点.

set transaction isolation level read uncommitted;

select top(10)
  PlanCreated       = qs.creation_time,
  ObjectName        = object_name(st.objectid),
  QueryPlan         = cast(qp.query_plan as xml),
  QueryText         = substring(st.text, 1 + (qs.statement_start_offset / 2), 1 + ((isnull(nullif(qs.statement_end_offset, -1), datalength(st.text)) - qs.statement_start_offset) / 2)),
  ExecutionCount    = qs.execution_count,
  TotalRW           = qs.total_logical_reads + qs.total_logical_writes,
  AvgRW             = (qs.total_logical_reads + qs.total_logical_writes) / qs.execution_count,
  TotalDurationMS   = qs.total_elapsed_time / 1000,
  AvgDurationMS     = qs.total_elapsed_time / qs.execution_count / 1000,
  TotalCPUMS        = qs.total_worker_time / 1000,
  AvgCPUMS          = qs.total_worker_time / qs.execution_count / 1000,
  TotalCLRMS        = qs.total_clr_time / 1000,
  AvgCLRMS          = qs.total_clr_time / qs.execution_count / 1000,
  TotalRows         = qs.total_rows,
  AvgRows           = qs.total_rows / qs.execution_count
from sys.dm_exec_query_stats as qs
  cross apply sys.dm_exec_sql_text(qs.sql_handle) as st
  cross apply sys.dm_exec_text_query_plan(qs.plan_handle, qs.statement_start_offset, qs.statement_end_offset) as qp
--order by ExecutionCount desc
--order by TotalRW desc
order by TotalDurationMS desc
--order by AvgDurationMS desc
;
Run Code Online (Sandbox Code Playgroud)

  • @ user3104183您需要针对提供问题的查询采取的操作必须根据具体情况决定.那里没有"抓住所有"的答案.我提供的查询当然不仅会捕获参数嗅探问题.它将捕获对您来说最贵的一次.**如果**你得出结论这是一个嗅探问题,有不同的方法来解决这个问题. (3认同)
  • @ user3104183添加了一个可用于答案的查询. (2认同)