SQL Server MAXDOP Recommendation Script
MAXDOP (max degree of parallelism) controls how many CPU threads SQL Server can use for a single parallel query. The default value of 0 allows SQL Server to use all available processors, which can cause large parallel queries to monopolise the server. This script reads the CPU topology from system DMVs and calculates Microsoft's recommended MAXDOP value for your instance.
Purpose and Overview
Setting MAXDOP incorrectly is one of the most common SQL Server configuration gaps. With MAXDOP 0 (unlimited), a single parallel query can consume every logical processor on the server, blocking other queries from getting CPU time. Setting MAXDOP too low, such as 1, disables parallelism entirely and slows down queries that benefit from parallel execution.
Microsoft's recommendation is based on CPU topology:
- Single NUMA node: set MAXDOP to the number of logical processors, up to 8
- Multiple NUMA nodes without hyperthreading: set MAXDOP to 8
- Multiple NUMA nodes with hyperthreading: set MAXDOP to the number of physical cores per NUMA node, up to 8
This script reads the topology automatically from sys.dm_os_sys_info and sys.dm_os_schedulers, calculates the recommended value, and compares it to the current sp_configure setting.
Code Breakdown
Script 1: MAXDOP Recommendation Calculator
1DECLARE @LogicalCPUs INT,
2 @PhysicalCores INT,
3 @NUMANodes INT,
4 @HyperthreadRatio INT,
5 @LogicalPerNUMA INT,
6 @PhysicalPerNUMA INT,
7 @MaxDOPRecommended INT;
8
9SELECT
10 @LogicalCPUs = cpu_count,
11 @HyperthreadRatio = hyperthread_ratio
12FROM sys.dm_os_sys_info;
13
14SELECT @NUMANodes = COUNT(DISTINCT parent_node_id)
15FROM sys.dm_os_schedulers
16WHERE status = 'VISIBLE ONLINE'
17 AND is_online = 1
18 AND parent_node_id < 64; -- exclude the DAC node (node 64)
19
20SET @PhysicalCores = @LogicalCPUs / NULLIF(@HyperthreadRatio, 0);
21SET @LogicalPerNUMA = @LogicalCPUs / NULLIF(@NUMANodes, 0);
22SET @PhysicalPerNUMA = @PhysicalCores / NULLIF(@NUMANodes, 0);
23
24-- Microsoft MAXDOP recommendation logic
25IF @NUMANodes = 1
26BEGIN
27 -- Single NUMA: use logical CPUs up to 8
28 SET @MaxDOPRecommended = CASE WHEN @LogicalCPUs < 8
29 THEN @LogicalCPUs
30 ELSE 8 END;
31END
32ELSE IF @HyperthreadRatio > 1
33BEGIN
34 -- Multiple NUMA with hyperthreading: use physical cores per NUMA, up to 8
35 SET @MaxDOPRecommended = CASE WHEN @PhysicalPerNUMA < 8
36 THEN @PhysicalPerNUMA
37 ELSE 8 END;
38END
39ELSE
40BEGIN
41 -- Multiple NUMA without hyperthreading: use logical per NUMA, up to 8
42 SET @MaxDOPRecommended = CASE WHEN @LogicalPerNUMA < 8
43 THEN @LogicalPerNUMA
44 ELSE 8 END;
45END
46
47SELECT
48 @@SERVERNAME AS server_name,
49 @LogicalCPUs AS logical_cpus_total,
50 @PhysicalCores AS physical_cores_total,
51 @NUMANodes AS numa_nodes,
52 @LogicalPerNUMA AS logical_cpus_per_numa,
53 @PhysicalPerNUMA AS physical_cores_per_numa,
54 CASE WHEN @HyperthreadRatio > 1
55 THEN 'Yes (' + CAST(@HyperthreadRatio AS VARCHAR) + ':1)'
56 ELSE 'No'
57 END AS hyperthreading,
58 @MaxDOPRecommended AS maxdop_recommended,
59 (SELECT CAST(value_in_use AS INT)
60 FROM sys.configurations
61 WHERE name = N'max degree of parallelism') AS maxdop_current,
62 CASE
63 WHEN (SELECT CAST(value_in_use AS INT)
64 FROM sys.configurations
65 WHERE name = N'max degree of parallelism')
66 = @MaxDOPRecommended
67 THEN 'OK — matches recommendation'
68 WHEN (SELECT CAST(value_in_use AS INT)
69 FROM sys.configurations
70 WHERE name = N'max degree of parallelism')
71 = 0
72 THEN 'WARNING — unlimited parallelism (MAXDOP 0)'
73 ELSE 'REVIEW — current setting differs from recommendation'
74 END AS status;
The script reads cpu_count and hyperthread_ratio from sys.dm_os_sys_info. When hyperthreading is enabled, cpu_count is the logical CPU count (physical cores × 2) and hyperthread_ratio is the multiplier (typically 2). Dividing cpu_count by hyperthread_ratio gives physical core count.
NUMA node count comes from sys.dm_os_schedulers, which shows one row per online scheduler. Filtering parent_node_id < 64 excludes node 64, which SQL Server reserves for the Dedicated Admin Connection (DAC) and should not be counted in the topology calculation.
The three-branch decision tree mirrors Microsoft's documented guidance. The final SELECT adds a status column that compares the calculated recommendation to the live value_in_use from sys.configurations.
Script 2: Apply the Recommended MAXDOP Setting
1-- Review the calculated recommendation from Script 1 before running this
2EXEC sys.sp_configure N'max degree of parallelism', 8; -- replace 8 with your recommended value
3RECONFIGURE WITH OVERRIDE;
4GO
5
6-- Verify the change
7SELECT
8 name,
9 value,
10 value_in_use,
11 description
12FROM sys.configurations
13WHERE name = N'max degree of parallelism';
sp_configure applies the change to the running instance immediately after RECONFIGURE WITH OVERRIDE. No SQL Server restart is required. Replace 8 with the output of @MaxDOPRecommended from Script 1 before executing.
Key Benefits and Use Cases
- Calculates the correct MAXDOP value automatically without manual CPU counting
- Surfaces the gap between the current setting and the Microsoft recommendation
- Works on any SQL Server version from 2008 onward
- Useful for new instance configuration, post-migration audits, and periodic health checks
- Covers single-NUMA, multi-NUMA, and hyperthreaded topologies correctly
Performance Considerations
- MAXDOP is a starting point, not a final answer: Microsoft's formula gives a sensible default, but heavily OLTP-loaded servers often benefit from testing with MAXDOP 4 or lower. Analytical/reporting workloads may benefit from a higher value.
- Cost threshold for parallelism: MAXDOP works alongside the
cost threshold for parallelismsetting. Raising the cost threshold (default: 5) to 25–50 prevents simple low-cost queries from going parallel, which is often a higher-value change than MAXDOP alone. - Database-scoped configuration: SQL Server 2016 and later supports per-database MAXDOP via
ALTER DATABASE SCOPED CONFIGURATION. You can set a lower MAXDOP for a high-concurrency OLTP database while leaving a higher value for a reporting database on the same instance. - Availability Groups: apply the same MAXDOP calculation independently on each AG node. Each node may have a different physical CPU count if AG nodes are heterogeneous VMs.
Practical Tips
To check MAXDOP across multiple instances from a Central Management Server:
1-- Run against all registered servers via CMS multi-server query
2SELECT
3 @@SERVERNAME AS server_name,
4 name,
5 value_in_use AS maxdop_current
6FROM sys.configurations
7WHERE name = N'max degree of parallelism';
To also set the cost threshold for parallelism alongside MAXDOP:
1-- Set cost threshold for parallelism (recommended: 25-50 for OLTP)
2EXEC sys.sp_configure N'cost threshold for parallelism', 50;
3RECONFIGURE WITH OVERRIDE;
4GO
After changing MAXDOP, monitor sys.dm_exec_query_stats for query CPU time changes over the following week. Look for queries where total_worker_time / execution_count increased significantly — those may need query-level hints or plan guides if the new MAXDOP is too restrictive for them.
Conclusion
MAXDOP is one of the first settings to review on any new or inherited SQL Server instance. This script removes the manual arithmetic by reading the CPU topology directly from system DMVs and applying Microsoft's documented recommendation logic automatically. Use the output as a starting point, then tune further based on observed workload behavior.
References
- BrentOzarULTD/SQL-Server-First-Responder-Kit on GitHub — sp_Blitz checks MAXDOP configuration as part of its standard health check output
- ktaranov/sqlserver-kit on GitHub — Community SQL Server script collection including server configuration checks
- Microsoft Docs: Configure the max degree of parallelism — Official guidance including the NUMA-based recommendation formula
- Microsoft Docs: sys.dm_os_sys_info (Transact-SQL) — cpu_count and hyperthread_ratio column reference
- Microsoft Docs: sys.dm_os_schedulers (Transact-SQL) — Scheduler and NUMA node metadata
- Microsoft Docs: ALTER DATABASE SCOPED CONFIGURATION — Per-database MAXDOP override available in SQL Server 2016+