DBA Scripts: Get MAXDOP Configuration

🔧Part of the DBA-Tools Project, copy/paste SQL Server scripts and health checks.In: Server & Configuration

Max Degree of Parallelism is one of the first things worth checking on any server, and one of the most commonly wrong. The default of 0 lets a single query use every scheduler on the box, which sounds efficient until 20 concurrent OLTP queries all decide to go parallel at once and end up fighting each other for CPU. Cost Threshold for Parallelism, MAXDOP’s quieter partner, is left at its ancient default of 5 in most environments, a number tuned for hardware from two decades ago.

This script reports both settings alongside the actual CPU topology, so you can tell at a glance whether the configuration matches the hardware it’s running on.


Why MAXDOP Configuration Matters

Both settings control when and how aggressively SQL Server splits a query across multiple CPU cores:

  • MAXDOP set too high (or left at 0 on a many-core box) lets individual queries consume more CPU than they should, starving concurrent workloads
  • MAXDOP set too low forces genuinely large queries to run single-threaded when they’d benefit from parallelism
  • Cost Threshold for Parallelism left at the default of 5 means even cheap, everyday queries go parallel, adding scheduling overhead for no real benefit
  • Misconfigured MAXDOP is one of the most common findings in any SQL Server performance review, and one of the cheapest to fix

When to Run This Script

  • Routine SQL Server health checks
  • After migrating to new hardware, a VM resize, or a core-count change
  • When diagnosing CXPACKET or CXCONSUMER waits
  • Reviewing a server you’ve just inherited

The Script

Run the following script against your SQL Server instance.

/*
Script Name : Get-MaxdopConfiguration
Category    : configuration-and-environment
Purpose     : Show MAXDOP and cost threshold settings alongside current CPU topology.
Author      : Peter Whyte (https://sqldba.blog/dba-scripts-get-maxdop-configuration/)
Requires    : VIEW SERVER STATE
*/
-- SAFE:ReadOnly
-- IMPACT:Low
SET NOCOUNT ON;

/* RECOMMENDED MAXDOP IS COMPUTED, NOT GUESSED. The rule below is Microsoft's published table
   on "Configure the max degree of parallelism Server Configuration Option", for SQL Server
   2016 and later, verified against the live page 2026-09-01:

     single NUMA node, <= 8 logical processors  -> at or under the logical processor count
     single NUMA node,  > 8 logical processors  -> 8
     multiple NUMA nodes, <= 16 per node        -> at or under the logical processors per node
     multiple NUMA nodes,  > 16 per node        -> half the processors per node, capped at 16

   NOTE the 2016 change. Through SQL Server 2014 the multi-NUMA branch simply capped at 8, and
   a great deal of advice still in circulation quotes that older table. This computes the
   CURRENT rule.

   Cost threshold deliberately gets no recommended number. Microsoft does not publish one:
   "The default value of 5 is a starting point, not a recommendation. On modern SQL Server
   systems, raising it can help to keep smaller OLTP queries executing with serial plans."
   The widely repeated "set it to 50" is community advice, not documentation, so this script
   reports the value and says whether it is still the default rather than inventing a target. */
SELECT
    (SELECT value_in_use FROM sys.configurations WHERE name = 'max degree of parallelism')     AS maxdop,
    (SELECT value_in_use FROM sys.configurations WHERE name = 'cost threshold for parallelism') AS cost_threshold_for_parallelism,
    osi.cpu_count                                                                               AS logical_cpu_count,
    osi.hyperthread_ratio,
    osi.cpu_count / osi.hyperthread_ratio                                                       AS physical_cpu_count,
    osi.scheduler_count                                                                         AS online_schedulers,
    osi.numa_node_count,
    osi.cpu_count / NULLIF(osi.numa_node_count, 0)                                              AS logical_cpus_per_numa_node,
    CASE
        WHEN osi.numa_node_count <= 1 THEN
            CASE WHEN osi.cpu_count <= 8 THEN osi.cpu_count ELSE 8 END
        ELSE
            CASE
                WHEN osi.cpu_count / osi.numa_node_count <= 16
                    THEN osi.cpu_count / osi.numa_node_count
                ELSE CASE WHEN (osi.cpu_count / osi.numa_node_count) / 2 > 16
                          THEN 16
                          ELSE (osi.cpu_count / osi.numa_node_count) / 2 END
            END
    END                                                                                         AS recommended_maxdop,
    CASE
        WHEN (SELECT value_in_use FROM sys.configurations WHERE name = 'max degree of parallelism')
             = CASE
                 WHEN osi.numa_node_count <= 1 THEN
                     CASE WHEN osi.cpu_count <= 8 THEN osi.cpu_count ELSE 8 END
                 ELSE
                     CASE
                         WHEN osi.cpu_count / osi.numa_node_count <= 16
                             THEN osi.cpu_count / osi.numa_node_count
                         ELSE CASE WHEN (osi.cpu_count / osi.numa_node_count) / 2 > 16
                                   THEN 16
                                   ELSE (osi.cpu_count / osi.numa_node_count) / 2 END
                     END
               END
        THEN 'OK: matches the current Microsoft guidance table'
        WHEN (SELECT value_in_use FROM sys.configurations WHERE name = 'max degree of parallelism') = 0
        THEN 'REVIEW: MAXDOP 0 lets one query use every processor. Microsoft: not recommended for most cases'
        ELSE 'REVIEW: does not match the guidance table for this CPU topology'
    END                                                                                         AS maxdop_verdict,
    CASE
        WHEN (SELECT value_in_use FROM sys.configurations WHERE name = 'cost threshold for parallelism') = 5
        THEN 'REVIEW: still the default 5. Microsoft calls 5 "a starting point, not a recommendation"'
        ELSE 'OK: moved off the default. Microsoft publishes no target value, so tune and measure'
    END                                                                                         AS cost_threshold_verdict
FROM sys.dm_os_sys_info AS osi;

The script reads the current MAXDOP and cost threshold values from sys.configurations, alongside logical/physical CPU counts, hyperthread ratio, scheduler count, and NUMA node count from sys.dm_os_sys_info, in a single row.


How To Run From The Repo

Clone DBA Tools, initialize and run the script:

# Clone dba-tools repo:
git clone https://github.com/peterwhyte-lgtm/dba-tools

# Initialize environment:
cd dba-tools
.\Initialize-Environment.ps1

# Check MAXDOP and cost threshold against actual CPU topology:
.\run.ps1 Get-MaxdopConfiguration

# To run against a remote sql server:
.\run.ps1 Get-MaxdopConfiguration -ServerInstance SQLSERVER01

This script lives in the repo at:


Example Output

One row, describing the whole instance. The lab box below has 8 logical CPUs on a single NUMA node with a hyperthread ratio of 8, so one physical core, and MAXDOP is set to 8. The script computes recommended_maxdop as 8 for that topology and returns maxdop_verdict of “OK: matches the current Microsoft guidance table”, while cost_threshold_verdict reports the threshold is still the default 5.

SSMS results grid showing Get-MaxdopConfiguration returning MAXDOP 8, recommended_maxdop 8 and a verdict of OK on an eight-CPU single-NUMA instance

Understanding the Results

maxdop
cost_threshold_for_parallelism
The two settings under review, read from sys.configurations as value_in_use, so this is what the engine is running with rather than what was last configured.
logical_cpu_count
hyperthread_ratio
physical_cpu_count
online_schedulers
The CPU topology the recommendation is computed from. physical_cpu_count is derived, logical divided by the hyperthread ratio, so it is the real core count rather than the logical one hyperthreading reports.Act when online_schedulers is lower than logical_cpu_count. Some processors are not available to SQL Server, through an affinity mask or licensing, and the topology you are tuning against is not the one the engine sees.
numa_node_count
logical_cpus_per_numa_node
The NUMA shape, and the number that actually drives the guidance table on multi-node hardware. On a single-node machine it equals the logical CPU count.
recommended_maxdop
Computed, not guessed. It applies Microsoft’s published table for SQL Server 2016 and later: a single NUMA node with 8 or fewer logical processors takes the processor count, a single node above 8 takes 8, multiple nodes with 16 or fewer per node take the per-node count, and above that, half the per-node count capped at 16.Act when you have inherited advice quoting the older rule. Through SQL Server 2014 the multi-NUMA branch simply capped at 8, and a great deal of guidance still in circulation repeats it.
maxdop_verdict
Compares the running value against recommended_maxdop and returns one of three things: OK when they match, a REVIEW for MAXDOP 0, which lets one query use every processor, or a REVIEW when the value does not match the table for this topology.Act when it reads REVIEW. It is a prompt to look, not an instruction to change. A deliberate non-standard value on a well-understood workload is a legitimate answer.
cost_threshold_verdict
Flags whether the threshold is still the default 5. It deliberately publishes no target number, because Microsoft does not publish one: the default is called “a starting point, not a recommendation”. The widely repeated “set it to 50” is community advice rather than documentation, so the script reports and does not prescribe.Act when it reads REVIEW. Raising the threshold can keep smaller OLTP queries on serial plans, but the right value comes from your own query cost distribution, measured.

How to Fix MAXDOP Configuration

-- Set MAXDOP (example: 8 logical CPUs or fewer per NUMA node)
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'max degree of parallelism', 8;
RECONFIGURE;

-- Raise cost threshold for parallelism from the outdated default of 5
EXEC sp_configure 'cost threshold for parallelism', 50;
RECONFIGURE;

Both changes take effect immediately, no restart required. Start conservatively, monitor CXPACKET/CXCONSUMER waits and query duration, then adjust.


Best Practices

  • Re-run this check after any hardware change: VM resize, core reassignment, new server.
  • Don’t copy a MAXDOP value from a blog post or another server without checking your own core count and NUMA layout first.
  • Review cost threshold alongside MAXDOP. Fixing one without the other often doesn’t move the needle.

Microsoft’s reference covers sys.configurations, sp_configure and sys.dm_os_sys_info in full.


Related Scripts

You may also find these scripts useful:


Frequently Asked Questions

What’s a good default MAXDOP value?

It follows NUMA layout, not raw core count. On SQL Server 2016 and later Microsoft’s guidance is: a single NUMA node with 8 or fewer logical processors, set MAXDOP to that count; a single node with more than 8, set it to 8. With multiple NUMA nodes, use the logical processors per node up to 16, and above 16 per node use half the per-node count with a ceiling of 16. The familiar “cap it at 8 per node” line is the SQL Server 2014 and earlier table, which is why it is still quoted so often. Check the topology first with CPU Topology and OS Configuration Checks, because soft-NUMA changes the answer on bigger boxes.

Does raising Cost Threshold for Parallelism disable parallelism?

No. It just raises the bar for when SQL Server considers a query “expensive enough” to justify going parallel. Cheap queries stay single-threaded; genuinely expensive ones still parallelize.


Summary

MAXDOP and cost threshold are two of the cheapest, lowest-risk configuration changes available, and two of the most commonly left at defaults that don’t match the hardware they’re running on.

Run this script as part of routine health checks, and always after a hardware or core-count change, since a MAXDOP value that was correct on the old server is rarely correct on the new one.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *