Causes and Checks for High CXPACKET Waits in SQL Server
Published 2026-08-13 · Updated 2026-08-13 · Last Verified 2026-08-13
Answer
CXPACKET is a wait type representing synchronization between tasks in a parallel query, and a large value by itself is not evidence of a problem. First check its share of total waits using `sys.dm_os_wait_stats`, and on SQL Server 2016 SP2 / 2017 CU3 and later, distinguish it from CXCONSUMER, into which harmless waits were split out. Whether there is actual impact should be judged by watching real-time waits with `sys.dm_os_waiting_tasks` and checking whether they are tied to an actually slow query.
Applicability of This Document
| Product | SQL Server / Amazon RDS for SQL Server / Azure SQL Managed Instance |
|---|---|
| Verified Versions | SQL Server 2008 and later (CXCONSUMER requires SQL Server 2016 SP2 / 2017 CU3 or later) |
| Environment | On-premises, EC2, Amazon RDS, Azure |
| Required Permissions | The read-only SQL requires VIEW SERVER STATE. `DBCC SQLPERF(..., CLEAR)` requires server-level permission |
| Execution Impact | The read-only SQL makes no changes. Clearing wait statistics resets the instance-wide statistics |
| Restart | Not required |
| Last Verified | 2026-08-13 |
Ready-to-Run Commands
- 対象
- SQL Server 2008 and later / Amazon RDS for SQL Server
- 権限
- VIEW SERVER STATE
- 変更作業
- None (read-only)
- Production実行
- Safe to run
-- 対象: 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` is a cumulative total since the instance started (or since it was last explicitly cleared). The waits in the exclusion list occur almost constantly as idle-type waits, so without excluding them they would fill up the top results. Even if CXPACKET ranks high in `pct_of_total`, that alone does not indicate a problem.
- 対象
- SQL Server 2012 and later (`sqlserver_start_time` column)
- 権限
- VIEW SERVER STATE
- 変更作業
- None (read-only)
- Production実行
- Safe to run
-- 対象: 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;Cumulative wait time grows in proportion to uptime. Do not conclude "CXPACKET is high" just by looking at the cumulative total for an instance that has been up for months. You need to look at the value per hour of uptime, or at the interval-based delta described below.
- 対象
- SQL Server 2008 and later (the `dop` column requires SQL Server 2016 or later)
- 権限
- VIEW SERVER STATE
- 変更作業
- None (read-only)
- Production実行
- Safe to run
-- 対象: 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` is the coordinator task for the parallel query, and all other values are parallel workers. A coordinator task waiting for a long time suggests the work may be unevenly distributed across workers (skew). The point of this query is to see whether something is waiting right now, not the cumulative total.
- 対象
- SQL Server 2008 and later / Amazon RDS for SQL Server
- 権限
- VIEW SERVER STATE
- 変更作業
- None (only creates a temp table)
- Production実行
- Safe to run
-- 対象: 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;This lets you measure "wait trends during a specific window" without clearing the instance-wide statistics, so prefer this approach in production. It does not affect measurements taken by other operations or monitoring tools.
- 対象
- SQL Server 2008 and later / Amazon RDS for SQL Server
- 権限
- Server-level permission (equivalent to sysadmin)
- 変更作業
- Yes (resets the instance-wide wait statistics to zero)
- Production実行
- Not recommended — also affects other monitoring tools' measurements
-- 対象: SQL Server 2008 以降 / Amazon RDS for SQL Server
-- 権限: サーバーレベルの権限(sysadmin 相当)
-- 変更作業: あり(インスタンス全体の待機統計をリセット)
-- Production 実行: 推奨しない。差分計測で代替できないか先に検討すること
DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);No data is lost, but the instance-wide cumulative wait statistics reset to zero. If other monitoring tools reference the same instance, or operations rely on comparison against past values, that baseline disappears. If the delta-measurement approach above meets your needs, use that instead.
How to Read the Results
| Column | Meaning | What to Check |
|---|---|---|
| wait_type | Type of wait | Check whether it distinguishes CXPACKET / CXCONSUMER / CXSYNC_PORT, etc. |
| waiting_tasks_count | Number of times this wait occurred | Look at whether it is frequent-but-short or rare-but-long |
| wait_time_ms | Cumulative wait time (including signal wait) | View it relative to instance uptime; the absolute value alone cannot be judged |
| resource_wait_ms | Resource wait time (wait_time_ms − signal_wait_time_ms) | Time actually spent waiting for a resource |
| signal_wait_time_ms | CPU scheduling wait time | A high share of the total suggests CPU saturation |
| pct_of_total | Share of total waits (computed) | Even if CXPACKET ranks high, separately confirm whether it is tied to an actually slow query |
| exec_context_id | Execution context ID of the parallel task | 0 is the coordinator task; if 0 waits for a long time, suspect an uneven workload |
| wait_duration_ms | How long the task is currently waiting | Whether any task is waiting a long time right now |
| dop | The actual degree of parallelism for the request (SQL Server 2016 and later) | Whether the degree of parallelism is higher than expected |
When to Use This
- When wait statistics are aggregated, CXPACKET is consistently near the top
- CPU usage is high, but individual query throughput does not improve
- The same query takes wildly different amounts of time from one run to the next
- Overall responsiveness degrades specifically during periods of high concurrency
Possible Causes (Most Likely First)
01
This is not actually a problem (parallel execution is working as intended)
CXPACKET necessarily occurs whenever tasks in a parallel query synchronize with each other. In systems with a lot of analytical queries, it is normal for it to rank high, and ranking high by itself does not indicate a problem. Rule this out first.
02
Cost Threshold for Parallelism is too low, causing even small queries to go parallel
The default value of 5 dates back to a very different era of hardware. In many environments, leaving it at this value causes queries that gain little from parallelism to get parallel plans anyway, adding coordination overhead with little benefit. That said, the right value depends on the workload and should not be changed without measurement.
03
Workload is unevenly distributed across parallel workers (skew)
In partitioned processing, if one worker handles a disproportionate number of rows, the other workers sit idle waiting. You can confirm this from the row-count distribution across threads in the actual execution plan.
04
Outdated statistics are causing an unnecessarily large parallel plan to be chosen
If the estimated row count is larger than the actual count, the estimated cost comes out higher, leading to a parallel plan. The fix here is updating statistics, not changing the degree of parallelism.
05
Missing indexes are causing a large scan to be parallelized
Without a suitable index, the query scans the entire table, and that scan is parallelized. Redesigning the index can eliminate the need for parallel execution altogether.
06
The CPU is saturated
If the share of `signal_wait_time_ms` is high, what's being waited on is not a resource but CPU scheduling order. Lowering the degree of parallelism can help, but the root cause is insufficient CPU or query inefficiency.
Diagnostic Steps
- 1
Get the top wait types
Read-onlyCheck the top 20 by cumulative value to understand where CXPACKET / CXCONSUMER rank.
- 2
Check instance uptime
Read-onlyCumulative values scale with uptime. Evaluate them alongside `sqlserver_start_time`.
- 3
Measure a delta for the relevant time window
Read-onlyTake a snapshot delta during the window when the problem occurs to produce wait trends specific to that window.
- 4
Identify an actually slow query
LowIf no slow query exists, no action is needed even if CXPACKET ranks high. Capture the slow query's execution plan before proceeding.
- 5
Observe real-time parallel waits
Read-onlyCheck `CX%` waits with `sys.dm_os_waiting_tasks` and see whether the coordinator task (`exec_context_id = 0`) is waiting for a long time.
- 6
Check statistics and indexes
Read-onlyCheck for a gap between estimated and actual row counts, and for any large scans. Only touch the degree-of-parallelism setting after this.
Solutions
Low-risk action that can be done immediately
Confirm whether there is real impact, and do nothing if there is none
Read-onlyEven if CXPACKET ranks high, no action is needed if every query is meeting its target time. Flattening the ranking of wait statistics is not, by itself, a goal.
Update statistics
MediumIf an unnecessary parallel plan was chosen due to a row-count estimation gap, updating statistics can change the plan. Check this before touching degree-of-parallelism settings.
Restrict the degree of parallelism for a specific query only
MediumIf the problem is limited to specific queries, control just those with `OPTION (MAXDOP n)`. This has no effect on the rest of the server.
Change requiring advance review
Review Cost Threshold for Parallelism
MediumConsider this once measurement confirms that even small queries are being parallelized. The right value depends on the workload, so decide by comparing execution plans and response times before and after the change.
Review MAXDOP
MediumSet an upper bound on the degree of parallelism at the server or database level. Because this changes execution plans server-wide, apply it gradually and measure the effect.
Redesign indexes
MediumWhere a large scan is being parallelized, adding a suitable index can eliminate the need for parallel execution entirely.
Change involving a restart or service impact
Clear wait statistics and re-measure
MediumThis resets the instance-wide cumulative statistics and destroys the baseline other monitoring tools rely on. Check first whether delta measurement can serve the same purpose.
Control the degree of parallelism with Resource Governor
Expert Review RequiredYou can control the degree of parallelism per workload group, but a misconfiguration can make a specific workload extremely slow. This also has edition restrictions, so it assumes a design review.
!Warnings
- CXPACKET ranking high among waits is not, by itself, evidence of a problem. The simplistic response of "CXPACKET is high, so set MAXDOP to 1" can drastically slow down analytical workloads that were functioning fine under parallel execution.
- From SQL Server 2016 SP2 / 2017 CU3 onward, some harmless waits during parallel execution were split out as CXCONSUMER. On these versions and later, do not treat CXCONSUMER the same as CXPACKET.
- `sys.dm_os_wait_stats` is cumulative since the instance started. Comparing absolute values between instances with different uptimes is meaningless.
- `DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR)` resets the instance-wide statistics. This also affects any other monitoring tool referencing the same instance.
- Changing degree-of-parallelism settings affects execution plans across the entire server. Do not make lowering the CXPACKET figure itself the goal.
Differences by Version and Environment
If This Doesn't Resolve It
Row-count distribution across threads in the execution plan
Check the actual execution plan for a significant imbalance in row counts across parallel threads — an imbalance indicates skew.
CPU usage and scheduler state
Check `runnable_tasks_count` in `sys.dm_os_schedulers` to see whether the CPU run queue is backing up.
PAGEIOLATCH-family waits
If I/O waits also rank high alongside CXPACKET, the root cause may lie on the storage side.
Memory grant waits (RESOURCE_SEMAPHORE)
Parallel queries request larger memory grants. If this wait co-occurs, memory also needs to be examined.
Basis and Limitations of This Document
Explanation based on official product documentation
A general procedure based on the public specifications of SQL Server's `sys.dm_os_wait_stats`, `sys.dm_os_waiting_tasks`, `sys.dm_exec_requests`, `sys.dm_os_sys_info`, and `DBCC SQLPERF`. The CXCONSUMER introduction is described as SQL Server 2016 SP2 / 2017 CU3, but check the version details of the target environment for the exact applicable build. It does not include measured values from any specific customer.
FAQ
Is a high volume of CXPACKET waits a problem?
Ranking high among waits is not, by itself, a problem. CXPACKET is evidence that parallel queries are running, and it is normal for it to rank high in environments with a lot of analytical workloads. It only becomes something to act on once there is a process actually exceeding its target time that is tied to this parallel wait.
What is the difference between CXPACKET and CXCONSUMER?
From SQL Server 2016 SP2 / 2017 CU3 onward, the consumer-side thread wait within parallel execution was split out as CXCONSUMER. CXCONSUMER is generally considered harmless, and treating it the same as CXPACKET leads to overreaction.
Can this be run in production?
The read-only SQL and the delta measurement can both be run in production. `DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR)` resets the instance-wide statistics, so it is not recommended in production — use delta measurement instead.
Does this work on AWS RDS?
The wait-statistics DMVs (`sys.dm_os_wait_stats`, `sys.dm_os_waiting_tasks`) can be queried as-is on Amazon RDS for SQL Server. However, since changing the degree of parallelism goes through a DB parameter group, procedures that assume `sp_configure` cannot be used as written.
What permissions are required?
The read-only SQL requires VIEW SERVER STATE. Clearing wait statistics requires server-level permission (equivalent to sysadmin), which may not be available on a managed service.
How should I interpret the results?
Evaluate by the delta during the window when the problem occurs, not by the ranking of cumulative values. From there, move on to reviewing the degree of parallelism only if the slow query's execution plan has a parallel operator and the row counts are unevenly distributed across threads.
Questions This Document Covers
- I want to know whether CXPACKET ranking high among waits is abnormal
- I want to know how to read wait events
- I want to check parallel query waits while they are happening
What the Risk Labels Mean
- Read-onlyDoes not change data or settings.
- LowImpact is limited, but permissions and load should be checked.
- MediumMay affect performance, locking, or cost.
- HighMay cause an outage, data loss, or require recovery work.
- Expert Review RequiredRequires a separate review before applying to production.
GIIP's Scope of Support
Wait events like CXPACKET are a metric best judged not by a single snapshot but by "how does this compare to normal." At GIIP, wait statistics are periodically captured as snapshots and accumulated in a form that supports comparing deltas across time windows. An AI agent detects deviations from the normal pattern and escalates to a human only when tied to an actually slowed-down query, avoiding number-first judgments like "CXPACKET ranks high, so lower the degree of parallelism."
Author & Technical Review
GIIP Production Operations Team
About 30 years of experience in designing, migrating, and operating large-scale web services, SQL Server, Oracle, AWS, and Azure. Experience includes 12 sets of x12large-class AWS RDS for SQL Server environments, an Oracle environment with roughly 120,000 tables, and migrating roughly 3TB from TiDB to Aurora MySQL. AI agents and human experts currently continue to monitor and operate multiple cloud databases and about 30 web services.
How to Check and Change MAXDOP and Cost Threshold for Parallelism in SQL Server
A procedure for checking and changing the degree of parallelism at the server, database, and query levels, along with the differences in scope and impact at each level.
sql-serverSQL to Check Statistics Last-Updated Time per Table in SQL Server
A read-only SQL script using sys.stats and STATS_DATE that lists the last-updated time and the number of rows modified since the last update, per table and statistic.
sql-serverSQL to Check for Long-Running Open Transactions in SQL Server
A procedure using the sys.dm_tran_active_transactions family of DMVs to identify abandoned transactions, including start time, session, and the last SQL statement executed.
monitoringWhat to Configure When Monitoring Servers and Databases 24/7
A checklist, organized by layer, of what to monitor, how to think about thresholds, the escalation structure, and why synthetic monitoring is necessary when designing 24/7 monitoring.
Related Services
Request an analysis of wait events
If you need to repeat the same checks across multiple environments, we can discuss your entire operations setup.
Request an analysis of wait events