これは、SQLServer内部計画キャッシュシリーズの一部です。このトピックに関するKalenの最初の投稿を必ずお読みください。
SQL Serverは約30年以上前から存在しており、私はSQLServerをほぼ同じくらい長い間使用してきました。私はこの素晴らしい製品の何年にもわたって(そして何十年にもわたって!)そしてバージョンの多くの変化を見てきました。これらの投稿では、SQL Serverの機能や側面のいくつかを、時には歴史的な視点とともにどのように見ているかを紹介します。
私の前の記事 、SQL Serverがクエリプランを再利用するために持っているさまざまなオプションを含め、SQLServerの診断について話しました。アドホック、準備、手順の3種類のクエリプランを検討しました。計画の不適切な再利用について説明しました。これは、SQLServerが間違った状況でパラメータースニッフィングを適用した場合に発生する可能性があります。プランが初期値に基づいているため、オプティマイザーがその値に適したプランを生成し、同じプランが別の値に使用された場合、そのプランは最適ではなくなる可能性があります。
では、パラメータスニッフィングが問題になる場合はどうすればよいでしょうか。 SQLServerに新しい計画を考えさせることができます。通常、新しい計画を立てる行為を「再コンパイル」と呼びますが、おそらく「再最適化」と呼ぶ必要があります。ただし、ほとんどの人は「再コンパイル」という用語を使用するため、ここではそれを使用します。
パラメータスニッフィングの不適切な使用が問題になる場合、簡単な解決策は、SQLServerに新しい計画を考え出すように指示することです。自動パラメータ化されたPREPAREDプランなどの個々のステートメントの場合、クエリにRECOMPILEヒントを追加できます。パラメータ化されたFORCED(前の記事で説明)を使用して、このクエリはパラメータ化されます。
SELECT * FROM dbo.newsales
WHERE SalesOrderID < @num;
このクエリを実行するたびに、@ numの値が大きく異なる可能性がある新しいプランを確実に取得したい場合は、次のようにRECOMPILEヒントを追加できます。
SELECT * FROM dbo.newsales
WHERE SalesOrderID < @num
OPTION (RECOMPILE);
ストアドプロシージャには、3つのオプションがあります。まず、RECOMPILEオプションを指定してプロシージャを実行することにより、再コンパイルが実際にパフォーマンスに役立つかどうかを確認できます。
EXEC get_sales_range 66666 WITH RECOMPILE;
このオプションを使用すると、この1回の実行に対してのみ新しいプランが生成されます。保存されず、確実に再利用されません。元のプランが再利用されていないため、プロシージャのsp_cacheobjects(前の投稿で説明)に示されているusecount値は増加しません。
次に、WITH RECOMPILEを実行すると役立つことがわかった場合は、RECOMPILEオプションを使用してプロシージャを再作成することを検討できます。その場合、プランは再利用されず、プロシージャはプランキャッシュにまったく表示されません。
DROP PROC IF EXISTS get_sales_range;GO
CREATE PROC get_sales_range
@num int
WITH RECOMPILE
AS
SELECT * FROM dbo.newsales
WHERE SalesOrderID < @num;
GO
私の簡単な小さな手順では、手順全体にWITHRECOMPILEオプションを使用するのが理にかなっているかもしれません。ただし、手順がより複雑な場合は、1つのステートメントが問題を引き起こしているため、手順全体を再コンパイルしても意味がない場合があります。したがって、3番目のオプションは、プロシージャ内のステートメントにRECOMPILEヒントを使用することです。したがって、次のようになります。
DROP PROC IF EXISTS get_sales_range;
GO
CREATE PROC get_sales_range
@num int
AS
SELECT * FROM dbo.newsales
WHERE SalesOrderID < @num
OPTION (RECOMPILE);
GO
これらのRECOMPILEオプションのいずれかを使用すると、SQLServerが要求に応じて新しい計画を作成するように強制できます。次に、SQL Server診断で、要求しないときに新しいプランが作成されるタイミング、つまり、既存のプランの自動再コンパイルがいつ発生するかを見ていきます。
プランの自動再コンパイルは、次の2種類の状況で発生します。
- 最初に、オプティマイザーが既存の計画が正しくないと判断した場合、通常はオブジェクト定義の変更が原因で、新しい計画を作成する必要があります。たとえば、TableAから選択するクエリの計画があり、いくつかの列を削除するか、TableAの列のデータ型を変更すると、SQL Serverはクエリを再コンパイルして、DDLの変更を反映する計画を作成します。
- 自動再コンパイルが発生する2番目の状況は、統計の変更により、SQLServerが計画が最適でなくなった可能性があると判断した場合です。ほとんどの場合、プランが最後にコンパイルされてからいずれかの列またはインデックスの統計が更新されている場合は、再コンパイルされます。しかし、これは別の問題につながります。統計はいつ更新されますか?関連する列の十分な行が変更されると、統計が自動的に更新されます。いくつで十分ですか?それについてはまもなく話します。
デフォルトでは、データベースオプションがデフォルトでオンになっているため、SQLServerは統計を自動的に更新します。ただし、データベースの所有者(またはすべてのデータベースで所有者として表示されるSQL「sa」)の場合は、オプションを変更できます。オプションの1つはAUTO_UPDATE_STATISTICSと呼ばれ、もう1つはAUTO_UPDATE_STATISTICS_ASYNCと呼ばれます。オプションAUTO_UPDATE_STATISTICSはtempdbデータベースでオンになっているため、新しいデータベースはすべてこのオプションを継承します。このオプションがオンで、クエリ実行エンジンがクエリの処理中に十分な数の行の変更を検出すると、統計が更新されている間実行が一時停止し、クエリが再コンパイルされます。もう1つのオプションであるAUTO_UPDATE_STATISTICS_ASYNCは、実行が一時停止しないため、クエリの実行時間への影響が少なくなる可能性がありますが、最適ではないプランを使用する可能性があります。 2番目のオプションでは、実行エンジンが統計を更新する必要があることを検出すると、更新を行うためにバックグラウンドスレッドが起動され、メインスレッドは元の統計と元の計画を使用してクエリの実行を続行します。影響を受けるテーブルにアクセスして更新された統計を確認する次のクエリは、クエリを再コンパイルしますが、実行の途中で一時停止して統計の更新を行うことはありません。
プランを再コンパイルするかどうかを制御するいくつかの状況といくつかのクエリヒントがあるので、フローチャートを示します。SQLServer内部のトレーニングクラス用に作成したフローチャートを共有します。
矢印は、SQLServerがバッチの処理を開始する場所です。最初に、バッチの計画がキャッシュにすでに存在するかどうかを確認し、答えが「いいえ」の場合は、右側の矢印に従って計画をコンパイルします。プランがキャッシュに入れられ、SQLServerが再起動します。はい、今回はプランをキャッシュに入れる必要があるため、下向きの矢印に従って、KEEPPLANというヒントが使用されているかどうかを確認します。はいの場合、SQL Serverはすぐに計画の実行を開始し、それ以上のチェックは行いません。
次の質問は、DDLの変更が行われたかどうかです。いいえの場合、この記事では説明できない他のいくつかの状況について質問します。実際、ここですべてのオプションを実行するつもりはありません。それはあなたにお任せします。ただし、質問や混乱がある場合は、ここのコメントセクションで質問するか、@sqlqueenでツイートしてください。右端の質問を指摘します:AUTO_STATS_ASYNCはオンですか?ここで、答えが「はい」の場合、2つのアクションがあることがわかります。 1つのブランチは既存のプランで実行を開始するだけで、もう1つは統計を更新するバックグラウンドスレッドですが、それ以外は何もしません。次のクエリでは、「新しい統計が利用可能ですか」の中央にある決定ボックスが表示され、「はい」と答えるはずなので、クエリが再コンパイルされます。
私が話す他の唯一のことは、「統計が古くなっていますか?」という質問です。これは基本的に、変更が多すぎるために統計が古くなっていることを意味します。これで、いくつが多すぎるかについて話すことができます。
非常に小さいテーブルにはさまざまな値が使用されますが、500行を超えるテーブルの場合、SQL Server 2016より前では、統計の基になっている列への変更の数が20を超えると、統計は「古い」と見なされていました。テーブル内の行数の%。したがって、1000行のテーブルの場合、これは200の挿入、200の更新、または200の削除を意味する可能性があります。 200行の変更、または5行がそれぞれ40回更新される可能性があります。 SQL Serverには、加えられた変更の数を報告する関数もあります。関心のある統計のstats_id番号を検索する必要があります。これは、統計がインデックスに属している場合はindex_idになります。 stats_idは、sys.statsというビューにあります。私のnewsalesテーブルでは、このクエリを使用して、SubTotal列のインデックスのstats_idが3であることを確認しています。
SELECT name, stats_id FROM sys.stats
WHERE object_id = object_id('newsales');
次に、その値を使用して変更の数を確認できます。最初にいくつかの行を更新しましょう:
UPDATE newsales
(影響を受ける1541行)
SET SubTotal = SubTotal * 0.9
WHERE SalesOrderID < 45200
SELECT * FROM sys.dm_db_stats_properties(object_id('newsales'), 3);
実際、20%は大きな数字です。また、多くのテーブルでは、更新された行の20%未満で、更新された統計の恩恵を受ける可能性があります。 2008R2 SP1以降、SQL Serverには、次のグラフに示すように、行数をスライディングスケールに変更するために使用できるトレースフラグが含まれていました。
SQL Server 2016以降、互換性レベル130以上である限り、スライディングスケールを使用したこの新しいアルゴリズムがデフォルトで使用されます。
クエリプランのほとんどの自動再コンパイルは、統計の変更によるものです。しかし、前述したように、再コンパイルの理由はそれだけではありません。ただし、これが最も一般的であるため、統計がいつどのように更新されるかを認識し、重要なテーブルの統計が十分な頻度で更新されて、最良の計画が得られるようにすることが非常に役立ちます。
パフォーマンスデータを自動的に分析してSQLサーバー診断を実行し、問題を迅速に解決して、パフォーマンスの低下が発生しているサーバーを特定します。今すぐSpotlightCloudの使用を開始します。