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이 많다"고 판단하지 마십시오. 가동 시간으로 나눈 시간당 값, 또는 뒤에서 설명할 구간 차이값으로 봐야 합니다.
- 対象
- 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`이 병렬 쿼리의 조정 작업(coordinator)이고, 그 외가 병렬 워커입니다. 조정 작업이 오래 대기하는 상태는 워커 간 처리량이 치우쳐 있을(스큐) 가능성을 나타냅니다. 누적값이 아니라 "지금 이 순간 대기하고 있는가"를 보는 것이 이 쿼리의 목적입니다.
- 対象
- 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과 동일하게 취급하면 과도한 대응으로 이어집니다.
운영 환경에서 실행할 수 있습니까?
참조용 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시간 모니터링을 설계할 때의 모니터링 대상·임계값 사고방식·에스컬레이션 체계·외부 모니터링의 필요성을 계층별로 정리한 체크리스트입니다.
関連サービス
대기 이벤트 분석을 요청하기
同じ確認を複数の環境で継続する必要がある場合は、運用体制ごと相談できます。
대기 이벤트 분석을 요청하기