SQL Server 2014的MAXDOP设置

我知道这个问题已经被问了很多次,并且也有答案,但是我仍然需要在这个主题上更多的指导。

以下是我从SSMS中得到的CPU详细信息:

CPU

以下是数据库服务器的任务管理器中的CPU选项卡:

CPU Tab

我根据以下公式将MAXDOP的设置保持在2。
declare @hyperthreadingRatio bit
declare @logicalCPUs int
declare @HTEnabled int
declare @physicalCPU int
declare @SOCKET int
declare @logicalCPUPerNuma int
declare @NoOfNUMA int
declare @MaxDOP int

select @logicalCPUs = cpu_count -- [Logical CPU Count]
    ,@hyperthreadingRatio = hyperthread_ratio --  [Hyperthread Ratio]
    ,@physicalCPU = cpu_count / hyperthread_ratio -- [Physical CPU Count]
    ,@HTEnabled = case 
        when cpu_count > hyperthread_ratio
            then 1
        else 0
        end -- HTEnabled
from sys.dm_os_sys_info
option (recompile);

select @logicalCPUPerNuma = COUNT(parent_node_id) -- [NumberOfLogicalProcessorsPerNuma]
from sys.dm_os_schedulers
where [status] = 'VISIBLE ONLINE'
    and parent_node_id < 64
group by parent_node_id
option (recompile);

select @NoOfNUMA = count(distinct parent_node_id)
from sys.dm_os_schedulers -- find NO OF NUMA Nodes 
where [status] = 'VISIBLE ONLINE'
    and parent_node_id < 64

IF @NoofNUMA > 1 AND @HTEnabled = 0
    SET @MaxDOP= @logicalCPUPerNuma 
ELSE IF  @NoofNUMA > 1 AND @HTEnabled = 1
    SET @MaxDOP=round( @NoofNUMA  / @physicalCPU *1.0,0)
ELSE IF @HTEnabled = 0
    SET @MaxDOP=@logicalCPUs
ELSE IF @HTEnabled = 1
    SET @MaxDOP=@physicalCPU

IF @MaxDOP > 10
    SET @MaxDOP=10
IF @MaxDOP = 0
    SET @MaxDOP=1

PRINT 'logicalCPUs : '         + CONVERT(VARCHAR, @logicalCPUs)
PRINT 'hyperthreadingRatio : ' + CONVERT(VARCHAR, @hyperthreadingRatio) 
PRINT 'physicalCPU : '         + CONVERT(VARCHAR, @physicalCPU) 
PRINT 'HTEnabled : '           + CONVERT(VARCHAR, @HTEnabled)
PRINT 'logicalCPUPerNuma : '   + CONVERT(VARCHAR, @logicalCPUPerNuma) 
PRINT 'NoOfNUMA : '            + CONVERT(VARCHAR, @NoOfNUMA)
PRINT '---------------------------'
Print 'MAXDOP setting should be : ' + CONVERT(VARCHAR, @MaxDOP)
我仍然看到与 CXPACKET 相关的高等待时间。我正在使用以下查询来获取这个信息:
WITH [Waits] AS
(SELECT
[wait_type],
[wait_time_ms] / 1000.0 AS [WaitS],
([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [ResourceS],
[signal_wait_time_ms] / 1000.0 AS [SignalS],
[waiting_tasks_count] AS [WaitCount],
100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage],
ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]
FROM sys.dm_os_wait_stats
WHERE [wait_type] NOT IN (
N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR',
N'BROKER_TASK_STOP', N'BROKER_TO_FLUSH',
N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
N'CHKPT', N'CLR_AUTO_EVENT',
N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE',
N'DBMIRROR_WORKER_QUEUE', N'DBMIRRORING_CMD',
N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
N'EXECSYNC', N'FSAGENT',
N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION',
N'HADR_LOGCAPTURE_WAIT', N'HADR_NOTIFICATION_DEQUEUE',
N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP',
N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE',
N'PWAIT_ALL_COMPONENTS_INITIALIZED',
N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP',
N'REQUEST_FOR_DEADLOCK_SEARCH', N'RESOURCE_QUEUE',
N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH',
N'SLEEP_DBSTARTUP', N'SLEEP_DCOMSTARTUP',
N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP',
N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT',
N'SP_SERVER_DIAGNOSTICS_SLEEP', N'SQLTRACE_BUFFER_FLUSH',
N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
N'SQLTRACE_WAIT_ENTRIES', N'WAIT_FOR_RESULTS',
N'WAITFOR', N'WAITFOR_TASKSHUTDOWN',
N'WAIT_XTP_HOST_WAIT', N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG',
N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT')
AND [waiting_tasks_count] > 0
)
SELECT
MAX ([W1].[wait_type]) AS [WaitType],
CAST (MAX ([W1].[WaitS]) AS DECIMAL (16,2)) AS [Wait_S],
CAST (MAX ([W1].[ResourceS]) AS DECIMAL (16,2)) AS [Resource_S],
CAST (MAX ([W1].[SignalS]) AS DECIMAL (16,2)) AS [Signal_S],
MAX ([W1].[WaitCount]) AS [WaitCount],
CAST (MAX ([W1].[Percentage]) AS DECIMAL (5,2)) AS [Percentage],
CAST ((MAX ([W1].[WaitS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgWait_S],
CAST ((MAX ([W1].[ResourceS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgRes_S],
CAST ((MAX ([W1].[SignalS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgSig_S]
FROM [Waits] AS [W1]
INNER JOIN [Waits] AS [W2]
ON [W2].[RowNum] <= [W1].[RowNum]
GROUP BY [W1].[RowNum]
HAVING SUM ([W2].[Percentage]) - MAX ([W1].[Percentage]) < 95; -- percentage threshold
GO

目前我的服务器的CXPACKET等待时间为63%:

Wait Statistics

我参考了多篇专家的建议,并查看了Microsoft关于MAXDOP的建议;然而,我并不确定这个值应该是多少才是最佳的。

我在同一个主题上找到了一个问题here,然而如果我按照Kin的建议,MAXDOP应该是4。在同一个问题中,如果我们按照Max Vernon的建议,它应该是3。

请提供您宝贵的建议。

版本:Microsoft SQL Server 2014 (SP3) (KB4022619) - 12.0.6024.0 (X64) Sep 7 2018 01:37:51 Enterprise Edition: Core-based Licensing (64-bit) on Windows NT 6.3 (Build 9600: ) (Hypervisor)

并行计算的成本阈值设置为70。在将其从默认值测试到25和50之后,CTfP被设置为70。当它是默认值(5)且MAXDOP为0时,CXPACKET的等待时间接近70%。

我在专家模式下执行了60秒的sp_blitzfirst,以下是结果和等待统计信息:

sp_blitzfirst


我同意@JaredKarney在他的回答中的评论:你想要修复/解决什么?你遇到了性能不佳的情况吗?为什么你认为高CXPACKET等待是不好的?你能否详细说明一下你的情况与其他关于这个问题的所有问题和答案有何不同? - John K. N.
@hot2use 是的,我遇到了性能问题,正在尝试查看可能导致性能下降的所有方面。我对CXPACKET等待统计数据不是很了解,所以希望从专家那里得到一些指导。 - Learning_DBAdmin
4个回答

虚假

以下是为什么等待统计报告很糟糕的原因:它没有告诉你服务器已经运行了多长时间。

我可以从你的 CPU 时间截图中看到:55天!

好吧,那么让我们来做些数学计算。

数学

一天有86,400秒。

SELECT (86400 * 55) seconds_in_55_days

那里的答案是?4,752,000

你总共有 452,488 秒的 CXPACKET。

SELECT 4752000 / 452488 AS oh_yeah_that_axis

这给你... 10(如果你进行实际计算,它更接近于9.5)。

所以,虽然CXPACKET可能占据了服务器等待时间的62%,但它只发生了大约10%的时间。

不要管它

你已经对设置做出了正确的调整,现在是时候进行实际的查询和索引优化,如果你想以有意义的方式改变数字。

其他考虑因素

CXPACKET可能来自于不均衡的并行处理:

在较新的版本中,它可能表现为CXCONSUMER:

在没有第三方监控工具的情况下,捕获自己的等待统计数据可能是值得的:


等待统计信息只是数字。如果您的服务器有任何操作,那么可能会出现某种等待。此外,根据定义,必须有一个等待时间最长的等待。这在没有某种规范化的情况下并没有意义。如果我正确地阅读了任务管理器的输出,那么您的服务器已经运行了55天。这意味着您每秒总共只有452000/(55*86400)=0.095秒的CXPACKET等待时间。此外,由于您正在使用SQL Server 2014,您的CXPACKET等待包括良性并行等待和可操作等待。请参见Making parallelism waits actionable以获取更多详细信息。基于您在此处提供的内容,我不会轻易得出MAXDOP设置不正确的结论。 我首先会测量吞吐量。这里真的有问题吗?我们无法告诉您如何做,因为它取决于您的工作负载。对于OLTP系统,您可以测量每秒事务数。对于ETL,则可以测量每秒加载的行数等。 如果您遇到了问题并且需要提高系统性能,那么在遇到该问题时应检查 CPU。如果 CPU 过高,则可能需要调整查询、增加服务器资源或减少活动查询的总数。如果 CPU 过低,则可能需要再次调整查询、增加活动查询的总数,或者可能存在一些等待类型负责。 如果您选择查看等待统计信息,则应仅在出现性能问题的期间查看它们。在过去的55天内查看全局等待统计信息在几乎所有情况下都不可行。它会给数据添加不必要的噪声,使您的工作更加困难。 完成适当的调查后,更改 MAXDOP 可能会对您有所帮助。对于您这样大小的服务器,我建议使用 MAXDOP 1、2、4 或 8。我们不能告诉您哪个最适合您的工作负载。您需要在更改 MAXDOP 前后监视吞吐量以得出结论。

  1. 你的“起始”maxdop应该是4;每个NUMA节点的最小核心数为8。你的公式是错误的。

  2. 高百分比的某种类型的等待并不意味着什么。在SQL中,一切都在等待,所以总会有某个最高的等待。唯一高cxpacket等待的意思是你有很高的并行度百分比。整体上CPU看起来并不高(至少根据提供的快照),所以可能不是问题。

  3. 在尝试解决问题之前,先定义问题。你试图解决什么问题?在这种情况下,似乎你将问题定义为高cxpacket等待的百分比,但光是这个本身并不是问题。


虚拟NUMA可以很容易地每个NUMA节点有2个核心。为什么你声称4是每个NUMA节点的最小核心数?你能解释一下你的意思吗? - Hannah Vernon

我认为最重要的问题是...你是否真的遇到了性能问题?如果答案是否定的,那么在没有问题的情况下为什么要寻找问题呢?

就像其他答案中所说的,所有的等待都是正常的,所有的CX等待只是指示您是否有并行查询。我要提醒的是,也许您应该看一下并行性的成本阈值设置,如果您对正在并行进行的查询有问题,比如执行很少任务的小查询在运行时可能变得更差,而本应并行进行的大型查询则因为所有性能较差的小型查询而被延迟。

如果没有问题,那就不要再试图制造一个问题。


请完整阅读问题,提供了并行性的成本阈值。 - Learning_DBAdmin