私たちのアプリケーションはSQL Server 2014を使用しており、プランキャッシュに関連する問題が発生しました。
パラメータ化されたクエリがあり、その実行プランはパラメータ値に依存します。サーバーは、場合によっては最適ではない実行プランをキャッシュし、その後のクエリすべてにそれを使用します。
詳細:
次の列で構成されるテーブルがあります。
_(
[Revision] [bigint] IDENTITY(1,1) NOT NULL,
[UserId] [uniqueidentifier] NOT NULL,
...A WHOLE LOT OF OTHER COLUMNS...
)
_
これらの2つの列の意味はかなり明確です。UserId
はレコードが属するユーザーのIDで、Revision
はレコードの自動インクリメントインデックスです。他の列は重要ではありませんが、存在して実行プランに影響を与えます。
テーブルには〜40.000.000行と〜200.000の個別のUserId
値が含まれているため、各ユーザーの平均レコード数は200です。行は更新されません。データの変更にはINSERTとDELETEのみを使用します。
アプリケーションは、このテーブルに対して次のクエリを実行します。
_SELECT * FROM SampleTable WHERE Revision > {someRevision} AND UserId = {someId}
_
テーブルには2つのインデックスがあります。
Revision asc
_UserId asc, Revision asc
_このクエリを手動で実行すると、実行プランがsomeRevision
の値に依存していることがわかります。
現在のリビジョンの最大値に比較的近い場合、サーバーは_Clustered Index Seek
_とともに_Seek Predicate: Revision > someRevision
_を使用します
閉じていない場合、サーバーはIndex Seek (NonClustered)
+ Key Lookup (Clustered)
を_Seek Predicate: UserId = someId AND Revision > someRevision
_とともに使用します。
私たちのアプリケーションはLinq-To-Sqlを使用し、パラメーター化されたクエリを生成します。これらは次のようになります。
_exec sp_executesql N'SELECT * FROM [SampleTable] AS [t0]
WHERE ([t0].[Revision] > @p0) AND ([t0].[UserId] = @p1)',N'@p0 bigint,@p1
uniqueidentifier',@p0=1234,@p1='bc38dd12-238c-41a2-9dea-bb12ce105e6d'
_
私は_dm_exec_cached_plans
_、_dm_exec_sql_text
_、_dm_exec_query_plan
_を使用し、サーバーがこのクエリの単一のプランをキャッシュに入れることを理解しました。したがって、対応する値がRevision
のクエリが最初に来た場合、_Clustered Index Seek
_を使用するプランはプランキャッシュに格納され、その後のすべてのクエリで使用されます。
これは、2番目の計画(Index Seek (NonClustered)
+ Key Lookup (Clustered)
)を使用して実行する必要があるクエリの論理読み取り(x10000)の過剰な数と許容できない実行時間につながります。
また、サーバーが計画間で切り替わるしきい値(転換点)が統計に依存していることにも気付きました。古くなっている場合、サーバーはRevision
指定された値より大きい。
さらに、類似のユースケースを持つ類似のテーブルの大規模なセットがあり、それらすべてに同じ問題があります。
この問題を解決するにはどうすればよいですか?
OPTION (RECOMPILE)
を使用することもできますが、これはLinq-To-Sqlでは簡単ではありませんが、パフォーマンスの点では本当に最適に見えません。
また、_sp_create_plan_guide
_を使用するか、Linq-To-SqlをさらにハックしてWITH (INDEX(...))
句を使用して2番目の計画を強制することもできますが、前述のように、同じコアを持つテーブルがたくさんあります構造なので、この方法は多くの手作業のように見えます。
一般的に、私の質問:
SQL Serverは、キャッシュに格納されているプランが特定のパラメーターに最適ではないことを理解し、それを使用しないのですか?
最適な実行プランがパラメーターに依存している場合、パラメーター化されたクエリを処理するためのいくつかのベストプラクティスはありますか?
これはパラメータースニッフィングと呼ばれ、Erland Sommarskogの叙事詩の記事 アプリで低速、SSMSで高速 で広くカバーされています。
ここでも正義を始めることはできませんが、サンプルソリューションには次のものがあります。
先に進んで取り組む Erlandの優れた記事 -今日の配当金を支払うだけでなく、この問題を何度も解決するため、キャリアを通じて成果を上げ続けるでしょう。今日のクエリで適切に機能するソリューションは、明日他のクエリで使用するソリューションとは大きく異なる可能性があります。