SQL ServerでCXPACKET待機が多いときの原因と確認方法
公開日 2026-08-13 · 更新日 2026-08-13 · 最終検証日 2026-08-13
結論
CXPACKET は並列クエリの各タスク間の同期待ちを表す待機で、値が大きいこと自体は障害の証拠になりません。まず `sys.dm_os_wait_stats` で全待機に占める割合を確認し、SQL Server 2016 SP2 / 2017 CU3 以降であれば無害な待機が分離された CXCONSUMER と区別してください。実害があるかどうかは、`sys.dm_os_waiting_tasks` でリアルタイムの待機を見て、実際に遅いクエリと結び付いているかで判断します。
この文書の適用条件
| 対象製品 | SQL Server / Amazon RDS for SQL Server / Azure SQL Managed Instance |
|---|---|
| 確認バージョン | SQL Server 2008 以降(CXCONSUMER は SQL Server 2016 SP2 / 2017 CU3 以降) |
| 適用環境 | オンプレミス、EC2、Amazon RDS、Azure |
| 必要権限 | 参照用SQLは VIEW SERVER STATE。`DBCC SQLPERF(..., CLEAR)` はサーバーレベルの権限が必要 |
| 実行影響 | 参照用SQLは変更なし。待機統計のクリアはインスタンス全体の統計値をリセットします |
| 再起動 | 不要 |
| 最終検証日 | 2026-08-13 |
そのまま実行できるコマンド
- 対象
- SQL Server 2008 以降 / Amazon RDS for SQL Server
- 権限
- VIEW SERVER STATE
- 変更作業
- なし(参照のみ)
- Production実行
- 可能
-- 対象: SQL Server 2008 以降 / Amazon RDS for SQL Server
-- 権限: VIEW SERVER STATE
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
SELECT TOP (20)
ws.wait_type,
ws.waiting_tasks_count,
ws.wait_time_ms,
ws.wait_time_ms - ws.signal_wait_time_ms AS resource_wait_ms,
ws.signal_wait_time_ms,
ws.max_wait_time_ms,
CAST(100.0 * ws.wait_time_ms
/ NULLIF(SUM(ws.wait_time_ms) OVER (), 0) AS decimal(5, 2)) AS pct_of_total
FROM sys.dm_os_wait_stats AS ws
WHERE ws.waiting_tasks_count > 0
-- 常時発生するアイドル・バックグラウンド待機を除外する
AND ws.wait_type NOT IN (
'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE', 'SLEEP_TASK',
'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH', 'WAITFOR', 'LOGMGR_QUEUE',
'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT',
'BROKER_TO_FLUSH', 'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT',
'DISPATCHER_QUEUE_SEMAPHORE', 'FT_IFTS_SCHEDULER_IDLE_WAIT',
'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'ONDEMAND_TASK_QUEUE',
'BROKER_EVENTHANDLER', 'SLEEP_BPOOL_FLUSH', 'DIRTY_PAGE_POLL',
'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', 'SP_SERVER_DIAGNOSTICS_SLEEP',
'QDS_ASYNC_QUEUE', 'HADR_FILESTREAM_IOMGR_IOCOMPLETION'
)
ORDER BY ws.wait_time_ms DESC;`sys.dm_os_wait_stats` はインスタンス起動時(または明示的なクリア時)からの累計値です。除外リストに載せた待機はほぼ常時発生するアイドル系のため、除かないと上位が埋まります。CXPACKET の `pct_of_total` が上位でも、それだけでは問題とは言えません。
- 対象
- SQL Server 2012 以降(`sqlserver_start_time` 列)
- 権限
- VIEW SERVER STATE
- 変更作業
- なし(参照のみ)
- Production実行
- 可能
-- 対象: SQL Server 2012 以降(sqlserver_start_time 列)
-- 権限: VIEW SERVER STATE
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
SELECT
sqlserver_start_time,
DATEDIFF(HOUR, sqlserver_start_time, GETDATE()) AS uptime_hours
FROM sys.dm_os_sys_info;累計待機時間は稼働時間に比例して増えます。起動から数か月経過したインスタンスの累計値だけを見て「CXPACKET が多い」と判断しないでください。稼働時間で割った1時間あたりの値、または後述の期間差分で見る必要があります。
- 対象
- SQL Server 2008 以降(`dop` 列は SQL Server 2016 以降)
- 権限
- VIEW SERVER STATE
- 変更作業
- なし(参照のみ)
- Production実行
- 可能
-- 対象: SQL Server 2008 以降(dop 列は SQL Server 2016 以降)
-- 権限: VIEW SERVER STATE
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
SELECT
wt.session_id,
wt.exec_context_id,
wt.wait_type,
wt.wait_duration_ms,
wt.blocking_session_id,
wt.blocking_exec_context_id,
wt.resource_description,
r.status,
r.command,
r.dop, -- SQL Server 2016 以降。それ以前の版では列を外すこと
r.total_elapsed_time,
txt.text AS batch_text
FROM sys.dm_os_waiting_tasks AS wt
LEFT JOIN sys.dm_exec_requests AS r
ON wt.session_id = r.session_id
OUTER APPLY sys.dm_exec_sql_text(r.sql_handle) AS txt
WHERE wt.wait_type LIKE 'CX%'
ORDER BY wt.session_id, wt.exec_context_id;`exec_context_id = 0` が並列クエリの調整タスク(コーディネーター)で、それ以外が並列ワーカーです。調整タスクが長く待っている状態は、ワーカー間で処理量が偏っている(スキュー)可能性を示します。累計値ではなく「今この瞬間に待っているか」を見るのがこのクエリの目的です。
- 対象
- SQL Server 2008 以降 / Amazon RDS for SQL Server
- 権限
- VIEW SERVER STATE
- 変更作業
- なし(一時テーブルの作成のみ)
- Production実行
- 可能
-- 対象: SQL Server 2008 以降 / Amazon RDS for SQL Server
-- 権限: VIEW SERVER STATE
-- 変更作業: なし(セッションスコープの一時テーブルのみ)
-- Production 実行: 可能
-- 1) 計測開始時点のスナップショットを取る
SELECT wait_type, waiting_tasks_count, wait_time_ms, signal_wait_time_ms
INTO #wait_snapshot
FROM sys.dm_os_wait_stats;
-- 2) 計測したい時間だけ待つ(例: 10分)
WAITFOR DELAY '00:10:00';
-- 3) 差分を取る
SELECT TOP (20)
w.wait_type,
w.waiting_tasks_count - s.waiting_tasks_count AS delta_tasks,
w.wait_time_ms - s.wait_time_ms AS delta_wait_ms,
w.signal_wait_time_ms - s.signal_wait_time_ms AS delta_signal_ms
FROM sys.dm_os_wait_stats AS w
INNER JOIN #wait_snapshot AS s
ON w.wait_type = s.wait_type
WHERE w.wait_time_ms - s.wait_time_ms > 0
ORDER BY delta_wait_ms DESC;
DROP TABLE #wait_snapshot;インスタンス全体の統計をクリアせずに「特定の時間帯だけの待機傾向」を測れるため、本番環境ではこちらを優先してください。他の運用・監視ツールの計測値にも影響しません。
- 対象
- SQL Server 2008 以降 / Amazon RDS for SQL Server
- 権限
- サーバーレベルの権限(sysadmin 相当)
- 変更作業
- あり(インスタンス全体の待機統計を0にリセット)
- Production実行
- 推奨しない。他の監視ツールの計測値にも影響する
-- 対象: SQL Server 2008 以降 / Amazon RDS for SQL Server
-- 権限: サーバーレベルの権限(sysadmin 相当)
-- 変更作業: あり(インスタンス全体の待機統計をリセット)
-- Production 実行: 推奨しない。差分計測で代替できないか先に検討すること
DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);データは失われませんが、インスタンス全体の累計待機統計が0に戻ります。同じインスタンスを参照している他の監視ツールや、過去との比較を前提とした運用がある場合、その基準が消えます。差分計測(前掲)で目的を満たせるなら、そちらを使ってください。
結果の読み方
| 列 | 意味 | 確認するポイント |
|---|---|---|
| wait_type | 待機の種類 | CXPACKET / CXCONSUMER / CXSYNC_PORT などの区別を確認する |
| waiting_tasks_count | その待機が発生した回数 | 回数が多く1回あたりが短いのか、回数は少ないが1回が長いのかを見る |
| wait_time_ms | 待機の累計時間(シグナル待ちを含む) | インスタンス稼働時間に対する比率で見る。絶対値だけでは判断できない |
| resource_wait_ms | リソース待ち時間(wait_time_ms − signal_wait_time_ms) | 実際にリソースを待っていた時間 |
| signal_wait_time_ms | CPUスケジュール待ち時間 | 全体に占める比率が高い場合、CPU の飽和を疑う |
| pct_of_total | 全待機に占める割合(算出値) | CXPACKET が上位でも、実際に遅いクエリと結び付くかを別途確認する |
| exec_context_id | 並列タスクの実行コンテキストID | 0 が調整タスク。0 が長く待つ場合は処理量の偏りを疑う |
| wait_duration_ms | 現在待機している時間 | リアルタイムで長時間待っているタスクがあるか |
| dop | そのリクエストの実際の並列度(SQL Server 2016 以降) | 想定より高い並列度になっていないか |
こういう状況で使います
- 待機事象を集計すると CXPACKET が常に上位に来る
- CPU 使用率は高いのに、個々のクエリのスループットが上がらない
- 同じクエリでも実行するたびに所要時間が大きくばらつく
- 同時実行数が増える時間帯だけ全体的に応答が遅くなる
考えられる原因(可能性の高い順)
01
そもそも問題ではない(並列実行が正常に機能している)
CXPACKET は並列クエリのタスク間同期で必ず発生します。分析系クエリが多いシステムでは上位に来るのが普通で、上位であること自体は異常を意味しません。まずこの可能性を排除してください。
02
Cost Threshold for Parallelism が低く、小さいクエリまで並列化されている
既定値の 5 は非常に古い時代の基準です。多くの環境ではこの値のままだと、並列化の恩恵が小さいクエリまで並列プランになり、調整コストだけが増えます。ただし適正値はワークロード次第で、計測なしに変更すべきではありません。
03
並列ワーカー間の処理量が偏っている(スキュー)
パーティション化された処理で特定のワーカーだけが大量の行を処理すると、他のワーカーが待ち続けます。実行プランの各スレッドの行数分布で確認できます。
04
統計情報が古く、不要に大きな並列プランが選ばれている
推定行数が実際より大きいと、コストが高く見積もられて並列プランが選ばれます。この場合の対処は並列度設定ではなく統計情報の更新です。
05
インデックスが不足し、大規模スキャンが並列化されている
適切なインデックスがないためテーブル全体をスキャンし、そのスキャンが並列化されている状態です。インデックス設計の見直しで並列実行自体が不要になります。
06
CPU が飽和している
`signal_wait_time_ms` の比率が高い場合、待っているのはリソースではなく CPU のスケジュール順です。並列度を下げると改善することがありますが、根本原因は CPU 不足またはクエリ効率です。
確認手順
- 1
待機事象の上位を取得する
参照のみ累計値の上位20件を確認し、CXPACKET / CXCONSUMER の位置づけを把握します。
- 2
インスタンス稼働時間を確認する
参照のみ累計値は稼働時間に比例します。`sqlserver_start_time` と併せて評価します。
- 3
対象時間帯で差分計測する
参照のみ問題が起きる時間帯にスナップショットの差分を取り、その時間帯だけの待機傾向を出します。
- 4
実際に遅いクエリを特定する
低遅いクエリが存在しないなら、CXPACKET が上位でも対応は不要です。遅いクエリの実行プランを取得してから次に進みます。
- 5
リアルタイムの並列待機を観測する
参照のみ`sys.dm_os_waiting_tasks` で `CX%` の待機を確認し、調整タスク(`exec_context_id = 0`)が長く待っていないかを見ます。
- 6
統計情報とインデックスを確認する
参照のみ推定行数と実際の行数の乖離、および大規模スキャンの有無を確認します。並列度設定を触るのはこの後です。
対応方法
すぐに実施できる低リスクの対応
実害の有無を確認して、無いなら何もしない
参照のみCXPACKET が上位でも、目標時間を満たしているクエリしかないなら対応は不要です。待機統計の順位を平準化すること自体は目的になりません。
統計情報を更新する
中推定行数の乖離が原因で不要な並列プランが選ばれている場合、統計更新でプランが変わることがあります。並列度設定より先に確認すべき項目です。
特定クエリだけ並列度を制限する
中問題のクエリが限定されているなら、`OPTION (MAXDOP n)` でそのクエリだけを制御します。サーバー全体への影響がありません。
事前検討が必要な変更
Cost Threshold for Parallelism を見直す
中小さいクエリまで並列化されていることが計測で確認できた場合に検討します。適正値はワークロードによって異なるため、変更前後の実行プランと応答時間を比較して決めます。
MAXDOP を見直す
中サーバー全体またはデータベース単位で並列度の上限を設定します。実行プランがサーバー全体で変わるため、段階的な適用と効果測定が前提です。
インデックスを設計し直す
中大規模スキャンが並列化されているケースでは、適切なインデックスの追加によって並列実行そのものが不要になります。
再起動・サービス影響を伴う変更
待機統計をクリアして再計測する
中インスタンス全体の累計統計がリセットされ、他の監視ツールの基準も失われます。差分計測で代替できないか先に検討してください。
リソースガバナーで並列度を制御する
専門家レビュー必須ワークロードグループ単位で並列度を制御できますが、設定を誤ると特定ワークロードが極端に遅くなります。エディション制約もあるため、設計レビューを前提とします。
!注意事項
- CXPACKET が待機の上位にあることは、それ自体では問題の証拠になりません。「CXPACKET が多い=MAXDOP を1にする」という短絡的な対応は、並列実行で成立していた分析系処理を大きく遅くする可能性があります。
- SQL Server 2016 SP2 / 2017 CU3 以降では、並列実行における無害な待機の一部が CXCONSUMER として分離されました。この版以降では CXCONSUMER を CXPACKET と同列に扱わないでください。
- `sys.dm_os_wait_stats` はインスタンス起動時からの累計です。稼働時間の異なるインスタンス同士を絶対値で比較しても意味がありません。
- `DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR)` はインスタンス全体の統計をリセットします。同じインスタンスを参照する他の監視ツールにも影響します。
- 並列度の設定変更はサーバー全体の実行プランに影響します。CXPACKET の数値を下げること自体を目的にしないでください。
バージョン・環境による違い
これで解決しない場合に確認すること
実行プランのスレッドごとの行数分布
実際の実行プランで、並列スレッド間の行数に大きな偏りが無いかを確認します。偏りがあればスキューです。
CPU 使用率とスケジューラの状態
`sys.dm_os_schedulers` の `runnable_tasks_count` を確認し、CPU 待ち行列が積み上がっていないかを見ます。
PAGEIOLATCH 系の待機
CXPACKET と併せて I/O 待機が上位なら、根本原因はストレージ側の可能性があります。
メモリ許可待ち(RESOURCE_SEMAPHORE)
並列クエリはメモリ許可を多く要求します。この待機が併発している場合はメモリ側の検討も必要です。
この文書の根拠と限界
製品の公式ドキュメントに基づく説明
SQL Server の `sys.dm_os_wait_stats`、`sys.dm_os_waiting_tasks`、`sys.dm_exec_requests`、`sys.dm_os_sys_info` および `DBCC SQLPERF` の公開仕様に基づく一般的な確認手順です。CXCONSUMER の追加時期は SQL Server 2016 SP2 / 2017 CU3 として記載していますが、適用ビルドの詳細は対象環境のバージョン情報で確認してください。特定顧客の実測値は含みません。
よくある質問
CXPACKET待機が多いのは問題ですか?
待機の上位にあること自体は問題ではありません。CXPACKET は並列クエリが動いている証拠であり、分析系の処理が多い環境では上位に来るのが通常です。実際に目標時間を超えている処理があり、それが並列待機と結び付いている場合にはじめて対応対象になります。
CXPACKETとCXCONSUMERの違いは何ですか?
SQL Server 2016 SP2 / 2017 CU3 以降で、並列実行の待機のうちコンシューマー側スレッドの待機が CXCONSUMER として分離されました。CXCONSUMER は一般に無害とされており、CXPACKET と同列に扱うと過剰な対応につながります。
Productionで実行できますか?
参照用のSQLと差分計測はいずれも本番環境で実行できます。`DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR)` はインスタンス全体の統計をリセットするため、本番では推奨しません。差分計測で代替してください。
AWS RDSでも使用できますか?
待機統計のDMV(`sys.dm_os_wait_stats`、`sys.dm_os_waiting_tasks`)は Amazon RDS for SQL Server でそのまま参照できます。ただし並列度の設定変更はDBパラメータグループ経由になるため、`sp_configure` を前提とした手順はそのままでは使えません。
どの権限が必要ですか?
参照用SQLには VIEW SERVER STATE が必要です。待機統計のクリアにはサーバーレベルの権限(sysadmin 相当)が必要で、マネージドサービスでは実行できない場合があります。
結果をどう判断しますか?
累計値の順位ではなく、問題が起きている時間帯の差分で評価してください。そのうえで、遅いクエリの実行プランに並列演算子があり、スレッド間の行数に偏りがある場合に並列度の見直しへ進みます。
この文書がカバーする質問
- CXPACKETが待機の上位に出るのは異常なのか知りたい
- 待機事象の見方を知りたい
- 並列クエリの待ちを実行中に確認したい
リスク表示の意味
- 参照のみデータと設定を変更しません。
- 低影響は限定的ですが、権限と負荷の確認が必要です。
- 中性能・ロック・コストに影響する可能性があります。
- 高障害・データ損失・復旧作業が発生する可能性があります。
- 専門家レビュー必須本番適用前に別途レビューが必須です。
GIIPの対応範囲
CXPACKET のような待機事象は、瞬間値ではなく「いつもと比べてどうか」で判断する指標です。GIIP では待機統計を定期的にスナップショットとして保持し、時間帯ごとの差分を比較できる形で蓄積しています。AIエージェントが平常時のパターンから外れた変化を検出し、実際に遅くなったクエリと結び付いたときだけ担当者に上げるため、「CXPACKET が上位だから並列度を下げる」といった数値先行の判断を避けられます。
執筆・技術検証
GIIP プロダクション運用チーム
大規模Webサービス、SQL Server、Oracle、AWS、Azureの設計・移行・運用に約30年従事。x12largeクラスのAWS RDS for SQL Server環境12セット、約12万テーブルのOracle環境、約3TBのTiDBからAurora MySQLへの移行を経験。現在も複数のクラウドデータベースと約30のWebサービスを、AIエージェントと人間の専門家が継続的に監視・運用しています。
SQL ServerのMAXDOPとCost Threshold for Parallelismを確認・変更する方法
サーバー・データベース・クエリの3階層で並列度を確認し変更する手順と、それぞれの適用範囲・影響範囲の違いを整理します。
sql-serverSQL Serverでテーブルごとの統計情報更新日時を確認するSQL
sys.stats と STATS_DATE で、テーブル・統計ごとの最終更新日時と更新後の変更行数を一覧化する参照専用SQLです。
sql-serverSQL Serverで長時間開いたままのトランザクションを確認するSQL
sys.dm_tran_active_transactions 系のDMVで、開始時刻・セッション・最後に実行したSQLまで含めて放置トランザクションを特定する手順です。
monitoringサーバーとデータベースを24時間監視するときに設定する項目
24時間監視を設計する際の監視対象・しきい値の考え方・エスカレーション体制・外形監視の必要性を、層ごとに整理したチェックリストです。
関連サービス
待機事象の分析を依頼する
同じ確認を複数の環境で継続する必要がある場合は、運用体制ごと相談できます。
待機事象の分析を依頼する