我读过很多关于参数嗅探的文章,但不清楚这是好是坏。任何人都可以用一个简单的例子来解释这一点。

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

提前致谢。

最佳答案

这很好,但有时可能很糟糕。

参数嗅探是关于查询优化器使用提供的参数的值来找出可能的最佳查询计划。许多选择中的一种并且很容易理解的是,是否应该扫描整个表以获取值,或者是否使用索引查找会更快。如果您的参数中的值具有高度选择性,优化器可能会构建一个带有搜索的查询计划,如果不是,则查询将对您的表进行扫描。

然后,查询计划被缓存并重用于具有不同值的连续查询。参数嗅探的坏处是当缓存计划不是这些值之一的最佳选择时。

样本数据:

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;
T 是一个有几千行的表,在 Value 上有一个非聚集索引。有一行的值为 1 ,其余的值为 2

示例查询:
select *
from T
where Value = @Value;

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

当嗅探到的值为 1 时,查询计划将如下所示:

sql-server - SQL Server - 参数嗅探-LMLPHP

当嗅探到的值为 2 时,它将如下所示:

sql-server - SQL Server - 参数嗅探-LMLPHP

在这种情况下,参数嗅探的坏部分发生在查询计划构建时嗅探 1 但稍后以 2 的值执行。

sql-server - SQL Server - 参数嗅探-LMLPHP

可以看到 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
;

关于sql-server - SQL Server - 参数嗅探,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20699393/

10-14 02:16