Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

SQL Server - Parameterschnüffeln

Es ist gut, kann aber manchmal schlecht sein.

Beim Parameter Sniffing geht es darum, dass der Abfrageoptimierer den Wert des bereitgestellten Parameters verwendet, um den bestmöglichen Abfrageplan zu ermitteln. Eine von vielen Optionen, die ziemlich einfach zu verstehen ist, ist, ob die gesamte Tabelle gescannt werden soll, um die Werte zu erhalten, oder ob es mit Indexsuchen schneller geht. Wenn der Wert in Ihrem Parameter sehr selektiv ist, erstellt der Optimierer wahrscheinlich einen Abfrageplan mit Suchvorgängen, und wenn dies nicht der Fall ist, führt die Abfrage einen Scan Ihrer Tabelle durch.

Der Abfrageplan wird dann zwischengespeichert und für aufeinanderfolgende Abfragen mit unterschiedlichen Werten wiederverwendet. Der schlechte Teil des Parameter-Sniffings ist, wenn der zwischengespeicherte Plan nicht die beste Wahl für einen dieser Werte ist.

Beispieldaten:

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 ist eine Tabelle mit ein paar tausend Zeilen mit einem nicht gruppierten Index auf Value. Es gibt eine Zeile, in der der Wert 1 ist und der Rest hat den Wert 2 .

Beispielabfrage:

select *
from T 
where Value = @Value;

Der Abfrageoptimierer hat hier die Wahl, entweder einen Clustered Index Scan durchzuführen und die where-Klausel für jede Zeile zu überprüfen, oder eine Indexsuche zu verwenden, um übereinstimmende Zeilen zu finden, und dann eine Schlüsselsuche durchzuführen, um die Werte aus den angeforderten Spalten zu erhalten die Spaltenliste.

Wenn der gesniffte Wert 1 ist Der Abfrageplan sieht folgendermaßen aus:

Und wenn der geschnüffelte Wert 2 ist es sieht so aus:

Der schlechte Teil des Parameter-Sniffings tritt in diesem Fall auf, wenn der Abfrageplan erstellt wird, um einen 1 zu schnüffeln aber später mit dem Wert 2 ausgeführt .

Sie können sehen, dass die Schlüsselsuche 2352 Mal ausgeführt wurde. Ein Scan wäre eindeutig die bessere Wahl.

Zusammenfassend würde ich sagen, dass Parameter-Sniffing eine gute Sache ist, die Sie versuchen sollten, so weit wie möglich zu erreichen, indem Sie Parameter für Ihre Abfragen verwenden. Manchmal kann es schief gehen und in diesen Fällen liegt es höchstwahrscheinlich an verzerrten Daten, die Ihre Statistiken durcheinander bringen.

Aktualisierung:

Hier ist eine Abfrage für ein paar dmvs, die Sie verwenden können, um herauszufinden, welche Abfragen auf Ihrem System am teuersten sind. Ändern Sie die Order by-Klausel, um andere Kriterien für das zu verwenden, wonach Sie suchen. Ich denke, dass TotalDuration ist ein guter Anfang.

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
;