SQL Server で終了しないように見えるクエリのトラブルシューティング
この記事では、クエリが完了しないと思われる問題のトラブルシューティング手順について説明します。または、クエリを完了するには数時間または数日かかる場合があります。
終わりのないクエリとは
このドキュメントでは、実行またはコンパイルを続けるクエリ、つまり CPU の増加を続けるクエリに焦点を当てています。 一部のリソースでブロックまたは待機しているクエリには適用されません (CPU は一定に保たれているか、ほとんど変更されません)。
重要
クエリの実行を完了するために残っている場合は、最終的に完了します。 数秒しかかからなかったり、数日かかる場合があります。
"終了しない" という用語は、クエリが実際に完了したときに完了しないという認識を表すために使用されます。
終わりのないクエリを識別する
クエリが継続的に実行されているか、ボトルネックでスタックしているかを特定するには、次の手順に従います。
次のクエリを実行します。
DECLARE @cntr int = 0 WHILE (@cntr < 3) BEGIN SELECT TOP 10 s.session_id, r.status, r.wait_time, r.wait_type, r.wait_resource, r.cpu_time, r.logical_reads, r.reads, r.writes, r.total_elapsed_time / (1000 * 60) 'Elaps M', SUBSTRING(st.TEXT, (r.statement_start_offset / 2) + 1, ((CASE r.statement_end_offset WHEN -1 THEN DATALENGTH(st.TEXT) ELSE r.statement_end_offset END - r.statement_start_offset) / 2) + 1) AS statement_text, COALESCE(QUOTENAME(DB_NAME(st.dbid)) + N'.' + QUOTENAME(OBJECT_SCHEMA_NAME(st.objectid, st.dbid)) + N'.' + QUOTENAME(OBJECT_NAME(st.objectid, st.dbid)), '') AS command_text, r.command, s.login_name, s.host_name, s.program_name, s.last_request_end_time, s.login_time, r.open_transaction_count, atrn.name as transaction_name, atrn.transaction_id, atrn.transaction_state FROM sys.dm_exec_sessions AS s JOIN sys.dm_exec_requests AS r ON r.session_id = s.session_id CROSS APPLY sys.Dm_exec_sql_text(r.sql_handle) AS st LEFT JOIN (sys.dm_tran_session_transactions AS stran JOIN sys.dm_tran_active_transactions AS atrn ON stran.transaction_id = atrn.transaction_id) ON stran.session_id =s.session_id WHERE r.session_id != @@SPID ORDER BY r.cpu_time DESC SET @cntr = @cntr + 1 WAITFOR DELAY '00:00:05' END
サンプル出力を確認します。
この記事のトラブルシューティング手順は、CPU が経過時間に比例して増加している次のような出力が、大幅な待機時間なしで発生する場合に特に適用されます。 一部の CPU バインド T-SQL 要求では論理読み取りがまったく行われない場合があるため(計算の実行や
WHILE
ループなど)、この場合、logical_reads
の変更は関係しないことに注意してください。session_id status cpu_time logical_reads wait_time wait_type 56 実行中 7038 101000 0 NULL 56 実行可能 12040 301000 0 NULL 56 実行中 17020 523000 0 NULL この記事は、CPU がわずかに変化したり変更されたりせず、セッションがリソースで待機している次のような待機シナリオを観察する場合は適用されません。
session_id status cpu_time logical_reads wait_time wait_type 56 一時停止中 0 3 8312 LCK_M_U 56 一時停止中 0 3 13318 LCK_M_U 56 一時停止中 0 5 18331 LCK_M_U
詳細については、 Diagnose の待機またはボトルネックを参照してください。
長いコンパイル時間
まれに、CPU が時間の経過と同時に継続的に増加しているが、クエリの実行によって決まるわけではないことがわかります。 代わりに、過度に長いコンパイル (クエリの解析とコンパイル) によって駆動される可能性があります。 そのような場合は、 transaction_name 出力列を確認し、 sqlsource_transform
の値を探します。 このトランザクション名はコンパイルを示します。
診断データの収集
- SQL Server 2008 - SQL Server 2014 (SP2 より前)
- SQL Server 2014 (SP2 以降) と SQL Server 2016 (SP1 より前)
- SQL Server 2016 (SP1 以降) と SQL Server 2017
- SQL Server 2019 以降のバージョン
SQL Server Management Studio (SSMS) を使用して診断データを収集するには、次の手順に従います。
最新のクエリ実行プラン XML をキャプチャします。
クエリ プランを確認して、遅さがどこから来る可能性があるかを明らかに示しているかどうかを確認します。 代表的な例は次のとおりです。
- テーブルまたはインデックスのスキャン (推定行を参照)。
- 巨大な外部テーブル データ セットによって駆動される入れ子になったループ。
- ループの内側に大きな分岐がある入れ子になったループ。
- テーブル スプール。
- 各行の処理に長い時間がかかる
SELECT
リスト内の関数。
クエリがいつでも高速に実行される場合は、比較する "高速" 実行 Actual XML 実行プラン をキャプチャできます。
収集されたプランを確認する方法
このセクションでは、収集されたデータを確認する方法について説明します。 SQL Server 2016 SP1 以降のビルドとバージョンで収集された複数の XML クエリ プラン (拡張機能 *.sqlplan を使用します。
実行プランを するには、次の手順に従います。
以前に保存したクエリ実行プラン ファイル (.sqlplan) を開きます。
実行プランの空白領域を右クリックし、 Compare Showplan を選択します。
比較する 2 番目のクエリ プラン ファイルを選択します。
演算子間を流れる多数の行を示す太い矢印を探します。 次に、矢印の前または後の演算子を選択し、2 つのプランの actual 行の数を比較します。
2 番目と 3 番目のプランを比較して、同じ演算子で最大の行フローが発生するかどうかを確認します。
次に例を示します。
解決方法
クエリで使用されるテーブルの統計が更新されていることを確認します。
クエリ プランで不足しているインデックスの推奨事項を探し、いずれかを適用します。
クエリを簡略化する目的で書き直します。
- より選択的な
WHERE
述語を使用して、事前に処理されるデータを減らします。 - 分割します。
- 一時テーブルにいくつかの部分を選択し、後で結合します。
- 行の目標を最適化するために非常に長い時間実行されるクエリの
TOP
、EXISTS
、およびFAST
(T-SQL) 削除します。 または、DISABLE_OPTIMIZER_ROWGOAL
hint を使用することもできます。 詳細については、「 Row Goals Gone Rogue」を参照してください。 - ステートメントを 1 つの大きなクエリに結合する場合は、共通テーブル式 (CTE) を使用しないでください。
- より選択的な
クエリ ヒントを使用して、より適切なプランを作成してみてください。
HASH JOIN
またはヒントMERGE JOIN
FORCE ORDER
ヒントFORCESEEK
ヒントRECOMPILE
- 強制的に実行できる高速クエリ プランがある場合は、
PLAN N'<xml_plan>'
を使用します
クエリ ストア (QDS) を使用して、適切な既知のプランが存在する場合、および SQL Server のバージョンでクエリ ストアがサポートされている場合に、適切な既知のプランを強制します。
待機またはボトルネックを診断する
このセクションは、問題が実行時間の長い CPU 駆動クエリでない場合のリファレンスとしてここに含まれています。 これを使用して、待機が原因で長いクエリのトラブルシューティングを行うことができます。
ボトルネックを待機しているクエリを最適化するには、待機の長さとボトルネックの場所 (待機の種類) を特定します。 待機の種類が確認されたら待機時間を短縮するか、完全に待機を排除します。
おおよその待機時間を計算するには、クエリの経過時間から CPU 時間 (ワーカー時間) を減算します。 通常、CPU 時間は実際の実行時間であり、クエリの有効期間の残りの部分は待機しています。
おおよその待機時間を計算する方法の例:
経過時間 (ミリ秒) | CPU 時間 (ms) | 待機時間 (ミリ秒) |
---|---|---|
3200 | 3000 | 200 |
7080 | 1000 | 6080 |
ボトルネックまたは待機を特定する
待機時間の長い履歴クエリ (全体の経過時間の >20% が待機時間など) を特定するには、次のクエリを実行します。 このクエリでは、SQL Server の開始以降にキャッシュされたクエリ プランのパフォーマンス統計が使用されます。
SELECT t.text, qs.total_elapsed_time / qs.execution_count AS avg_elapsed_time, qs.total_worker_time / qs.execution_count AS avg_cpu_time, (qs.total_elapsed_time - qs.total_worker_time) / qs.execution_count AS avg_wait_time, qs.total_logical_reads / qs.execution_count AS avg_logical_reads, qs.total_logical_writes / qs.execution_count AS avg_writes, qs.total_elapsed_time AS cumulative_elapsed_time FROM sys.dm_exec_query_stats qs CROSS apply sys.Dm_exec_sql_text (sql_handle) t WHERE (qs.total_elapsed_time - qs.total_worker_time) / qs.total_elapsed_time > 0.2 ORDER BY qs.total_elapsed_time / qs.execution_count DESC
待機時間が 500 ミリ秒を超える現在実行中のクエリを特定するには、次のクエリを実行します。
SELECT r.session_id, r.wait_type, r.wait_time AS wait_time_ms FROM sys.dm_exec_requests r JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id WHERE wait_time > 500 AND is_user_process = 1
クエリ プランを収集できる場合は、SSMS の実行プランのプロパティからWaitStats を確認します。
- Include Actual Execution Plan on を使用してクエリを実行します。
- [実行プラン]タブで、左端の演算子右クリックします。
- Propertiesを選択しWaitStatsプロパティを選択します。
- WaitTimeMs と WaitType を確認します。
PSSDiag/SQLdiag または SQL LogScout LightPerf/GeneralPerf シナリオに慣れている場合は、いずれかのシナリオを使用してパフォーマンス統計を収集し、SQL Server インスタンスで待機中のクエリを特定することを検討してください。 収集したデータ ファイルをインポートし、 SQL Nexus を使用してパフォーマンス データを分析できます。
待機の排除または削減に役立つ参照
各待機の種類の原因と解決策は異なります。 すべての待機の種類を解決する一般的な方法は 1 つもありません。 待機の種類に関する一般的な問題のトラブルシューティングと解決に関する記事を次に示します。
- ブロックの問題を理解して解決する (LCK_M_*)
- Azure SQL Database のブロックの問題の概要と解決策
- I/O の問題 (PAGEIOLATCH_*、WRITELOG、IO_COMPLETION、BACKUPIO) による SQL Server のパフォーマンス低下のトラブルシューティング
- SQL Server での最終ページ挿入PAGELATCH_EX 競合を解決する
- メモリ許可の説明と解決策 (RESOURCE_SEMAPHORE)
- 待機の種類に起因する低速クエリASYNC_NETWORK_IOトラブルシューティングする
- Always On 可用性グループを使用した高HADR_SYNC_COMMIT待機の種類のトラブルシューティング
- しくみ: CMEMTHREAD とデバッグ
- 並列処理待機を実行可能にする (CXPACKET と CXCONSUMER)
- THREADPOOL 待機