パラメータ スニッフィングに関する多くの記事を読みましたが、これが良いか悪いかは明確ではありません。誰でも簡単な例でこれを説明できますか。
間違った計画が特定のステートメントに割り当てられたことを自動的に検出する方法はありますか?
前もって感謝します。
パラメータ スニッフィングに関する多くの記事を読みましたが、これが良いか悪いかは明確ではありません。誰でも簡単な例でこれを説明できますか。
間違った計画が特定のステートメントに割り当てられたことを自動的に検出する方法はありますか?
前もって感謝します。
それは良いことですが、時には悪いこともあります。
パラメーター スニッフィングは、提供されたパラメーターの値を使用して可能な限り最適なクエリ プランを把握するクエリ オプティマイザーに関するものです。多くの選択肢の 1 つであり、非常に理解しやすいのは、テーブル全体をスキャンして値を取得するか、インデックス シークを使用すると高速になるかということです。パラメーターの値が非常に選択的である場合、オプティマイザーはおそらくシークを使用してクエリ プランを作成し、そうでない場合、クエリはテーブルのスキャンを実行します。
その後、クエリ プランがキャッシュされ、異なる値を持つ連続したクエリに再利用されます。パラメータ スニッフィングの悪い点は、キャッシュされたプランがこれらの値の 1 つに最適でない場合です。
サンプルデータ:
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 つ1
あり、残りの行には value があります2
。
サンプルクエリ:
select *
from T
where Value = @Value;
ここでクエリ オプティマイザーが選択できるのは、クラスター化インデックス スキャンを実行してすべての行に対して where 句をチェックするか、インデックス シークを使用して一致する行を見つけてから、キー ルックアップを実行して、要求された列から値を取得することです。列リスト。
スニッフィングされた値が1
クエリ プランの場合、次のようになります。
スニッフィングされた値が次の2
ようになると、次のようになります。
この場合のパラメーター スニッフィングの悪い部分は、クエリ プランが をスニッフィングして構築さ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
;