关于 CPU 时间和经过时间计算的混淆

Ehs*_*jad 4 performance sql-server optimization query-performance

我已经在不同的在线论坛上搜索过,但对此我还没有得到任何明确的答案。

问题是我有一个查询,它的 CPU 时间比 Elapsed 时间大 4 倍。我还有一个查询,其中 Elapsed time 大于 CPU 时间。我不明白为什么会这样。您能否解释一下,这两种情况下实际发生了什么?

一个例子是我的查询有 4000 毫秒的 CPU 时间,而经过的时间只有 1000 毫秒,而一个查询的经过时间是 2 毫秒,而 CPU 时间是 0 毫秒。

根据以下帖子:

/sf/ask/449351871/

更高的 CPU 时间和更低的运行时间是由于 SQL 服务器的超线程或多核利用率,但我无法找到任何相关的真实信息。另一个混淆是相反的情况。例如,当 CPU 时间为 0ms 时,经过的时间怎么可能是 4ms?

对此的任何详细答案将不胜感激。

Joe*_*ish 7

如果查询的 CPU 时间比经过的时间多,则意味着它包含一个并行执行的区域。CPU 时间与经过时间的比率取决于许多因素,包括查询可用的 CPU 数量、查询等待的内容以及等待的时间(部分取决于服务器工作负载)以及查询的效率并行的优势。对于最后一点参考阿姆达尔定律的示例:

例如,如果一个程序需要使用单个处理器内核 20 小时,并且程序中需要 1 小时执行的特定部分无法并行化,而其余 19 小时 (p = 0.95) 的执行时间可以并行化,则无论有多少处理器专门用于并行执行该程序,最小执行时间都不能少于关键的一小时。

许多查询将具有并行运行的区域和串行运行的区域。如果大部分工作在并行区域中完成,并且工作在调度程序之间平均分配,那么您可能会看到 CPU 时间与运行时间的比率很高。

下面是一个人为的查询示例,它可以在 SQL Server 中极其高效地并行化。首先请注意,我正在测试的机器有 4 个内核,并且所有内核都可用于查询。从任务管理器:

任务监视器

从 SQL Server:

SELECT cpu_count -- result of 4
FROM sys.dm_os_sys_info;
Run Code Online (Sandbox Code Playgroud)

现在我将 100 行放入一个临时表,将 100k 行放入另一个:

CREATE TABLE #OUTER_TABLE (ID INT NOT NULL);

-- insert 100 integers from 1 - 100
INSERT INTO #OUTER_TABLE
SELECT N
FROM dbo.getNums(100);
    
CREATE TABLE #INNER_TABLE (COL VARCHAR(8000));

-- insert 100000 rows
INSERT INTO #INNER_TABLE
SELECT REPLICATE('Z', 800)
FROM dbo.getNums(100000);

SET STATISTICS TIME ON;
Run Code Online (Sandbox Code Playgroud)

这是我要测试的查询:

SELECT ot.ID, it.COL
FROM 
(
    SELECT TOP 100 ID
    FROM #OUTER_TABLE
) ot
CROSS APPLY (
    SELECT MAX(COL) COL
    FROM #INNER_TABLE
    WHERE COL <> CAST(ot.ID AS VARCHAR(8000))
) it
 -- TF 8649 is only used for demonstration purposes and should not be used in production
OPTION (MAXDOP 4, QUERYTRACEON 8649);
Run Code Online (Sandbox Code Playgroud)

这有点混乱,所以让我们来看看它。根据表中的数据,查询将返回所有 100 行#OUTER_TABLE以及来自 的任何值#INNER_TABLE。但是,我没有将这些信息提供给查询优化器,因此它会做很多工作来获得我们期望的结果。总的来说,查询很愚蠢,但重要的是查询的执行方式。这是一个屏幕截图:

实际查询计划

我将跳过为什么我这样写查询,因为它与问题无关。但是,需要注意的一件事是,将 100 行#OUTER_TABLE拆分为四个线程,每个线程 25 行。每个线程对#INNER_TABLE每行进行一次完整扫描,因此 4 个线程每个执行 25 次扫描。循环内部的查询部分几乎完成了您所期望的所有工作(外部部分扫描 100 行,但内部部分扫描 100 * 100000 = 10000000 行)。

当我使用MAXDOP 4(使用所有四个核心)运行查询时,我得到以下结果SET STATISTICS TIME ON

SQL Server 执行时间:

CPU 时间 = 24500 毫秒,已用时间 = 6357 毫秒。

使用 MAXDOP 2(仅使用两个内核)运行会得到以下结果:

SQL Server 执行时间:

CPU 时间 = 23422 毫秒,已用时间 = 11864 毫秒。

使用 MAXDOP 1(非并行)运行会得到以下结果:

SQL Server 执行时间:

CPU 时间 = 23313 毫秒,已用时间 = 23361 毫秒。

如您所见,查询可以非常有效地利用并行性。野外的大多数查询不会有这么高的 CPU 时间与经过时间的比率(在考虑 DOP 之后)。

例如,当经过的时间可能大于 CPU 时间时,请考虑将结果发送到客户端(例如 SQL Server Management Studio 结果网格)所需的时间。发送这些结果的时间将包含在经过的时间中,但不会严重影响 CPU 时间。服务器从内存或磁盘读取结果的工作将影响 CPU 时间和运行时间。向客户端发送结果所花费的时间是一种等待事件(通常为ASYNC_NETWORK_IO)。SQL Server 查询可能处于等待状态的原因很多,而且并非所有原因都以相同的方式影响 CPU 利用率。

假设我运行这个查询:

SELECT * FROM INNER_TABLE;
Run Code Online (Sandbox Code Playgroud)

将结果发送给客户端会导致 CPU 时间为 94 毫秒,运行时间为 749 毫秒。我可以使用sys.dm_exec_session_wait_stats查看会话的等待事件:

???????????????????????????????????????????????????????????????????????????????????????????????????????
?       wait_type       ? waiting_tasks_count ? wait_time_ms ? max_wait_time_ms ? signal_wait_time_ms ?
???????????????????????????????????????????????????????????????????????????????????????????????????????
? ASYNC_NETWORK_IO      ?               19444 ?          662 ?               12 ?                  67 ?
? MEMORY_ALLOCATION_EXT ?                  37 ?            0 ?                0 ?                   0 ?
???????????????????????????????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

我还可以在不同的会话中运行查询,而无需将结果集返回给客户端。在一项测试中,这需要 47 毫秒的 CPU 时间和 60 毫秒的运行时间。以下是该查询的等待会话,它们与另一个非常不同:

???????????????????????????????????????????????????????????????????????????????????????????????????????
?       wait_type       ? waiting_tasks_count ? wait_time_ms ? max_wait_time_ms ? signal_wait_time_ms ?
???????????????????????????????????????????????????????????????????????????????????????????????????????
? ASYNC_NETWORK_IO      ?                 185 ?            2 ?                0 ?                   1 ?
? SOS_SCHEDULER_YIELD   ?                   7 ?            0 ?                0 ?                   0 ?
? MEMORY_ALLOCATION_EXT ?                  40 ?            0 ?                0 ?                   0 ?
???????????????????????????????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)