在单行 INSERT 语句期间围绕 SOS_SCHEDULER_YIELD 行为的实例性能变化

Cad*_*oux 9 sql-server wait-types

我在 VM 中有一个共享的 SQL Server 2016 开发实例,与(应该是)相同 VM 上的共享 SQL Server 2012 开发实例相比,该实例的 INSERT 性能较差。它与我的本地 SQL Server 2016 开发实例相比也很差,因此我目前假设该实例存在一些古怪的问题,因为该服务器上的大多数其他操作似乎相当稳定。

它们是以下形式的 1000 个单独的插入语句:

INSERT INTO tblname (columnlist) VALUES (literals);

没有明确的交易。

我将这些 INSERT 包装在扩展事件中,以便我可以看到一些环境之间的差异。我还制作了一个运行循环而不是文字的版本。循环的性能在受影响的系统上明显不同,我不包括下面的循环版本代码 - 它执行一致,但在执行该脚本之前我确实删除并重新创建了数据库,结果也包括在内。

我已将系统设置为可重现并轻松推送到任何系统进行比较。我正在运行命令脚本和 sqlcmd 中的所有内容。首先运行安装脚本以创建数据库、表和 XE 会话。然后运行第二个脚本,启动跟踪,执行 1000 条 INSERT VALUES 语句,停止跟踪并使用https://www.sqlskills.com/blogs/paul/capturing-wait-stats-for-a-single-显示结果手术/

我已经注意到这个特定的 SQL Server 2016 在 SOS_SCHEDULER_YIELD 周围有非常不同的特征,我想知道这是否已经指出潜在的问题可能是什么。此外,我似乎已将此问题与在此服务器上的默认值中使用 GETDATE 和 GETUTCDATE 隔离开来。

性能测试.cmd:

DEL *.xel
echo Running setup.sql
sqlcmd -o "setup.sql.output-1.txt" -I -b -l 120 -t 300 -S %SQLINSTANCE% -U sa -P %PASSWORD% -d master -i .\setup.sql
echo Running perftest.sql
sqlcmd -o "perftest.sql.output-1.txt" -I -b -l 120 -t 300 -S %SQLINSTANCE% -U sa -P %PASSWORD% -d InsertPerfTest -i .\perftest.sql
echo Running setup.sql
sqlcmd -o "setup.sql.output-2.txt" -I -b -l 120 -t 300 -S %SQLINSTANCE% -U sa -P %PASSWORD% -d master -i .\setup.sql
echo Running perftestloop.sql
sqlcmd -o "perftestloop.sql.output-1.txt" -I -b -l 120 -t 300 -S %SQLINSTANCE% -U sa -P %PASSWORD% -d InsertPerfTest -i .\perftestloop.sql
COPY setup.sql.output-1.txt /A + perftest.sql.output-1.txt /A + setup.sql.output-2.txt /A + perftestloop.sql.output-1.txt /A combined-output.txt /A
Run Code Online (Sandbox Code Playgroud)

设置.sql:

SET NOCOUNT ON;
GO

PRINT '-------------------- SCRIPT START--------------------------------';
SELECT TEST = 'setup.sql', SERVERNAME = CAST(@@SERVERNAME AS nvarchar(32)), [VERSION] = @@VERSION, DATABASENAME = CAST(DB_NAME() AS nvarchar(32));
GO

IF EXISTS (SELECT * FROM sys.databases WHERE [name] = 'InsertPerfTest')
BEGIN
    EXEC msdb.dbo.sp_delete_database_backuphistory @database_name = N'InsertPerfTest';
    ALTER DATABASE [InsertPerfTest] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE [InsertPerfTest];
END
GO

CREATE DATABASE [InsertPerfTest]
ON (
    NAME = 'PerfTest_Data'
    , FILENAME = 'C:\perftest\PerfTest_Data.mdf'
    , SIZE = 32MB
    , MAXSIZE = 32MB
)
LOG ON (
    NAME = 'PerfTest_Log'
    , FILENAME = 'C:\perftest\PerfTest_Log.ldf'
    , SIZE = 32MB
    , MAXSIZE = 32MB
)
;
GO

ALTER DATABASE [InsertPerfTest] SET RECOVERY SIMPLE;
GO

USE [InsertPerfTest];
GO

ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 1;
GO

CREATE TABLE [TrivialTest](
    [MapRowID] [int] IDENTITY(1,1) NOT NULL,
    [ItemNumber] [int] NOT NULL,

    [ItemText] [nvarchar](max) NULL,

    [UpdateDateTime] [datetimeoffset](7) NOT NULL CONSTRAINT [DF_Trivial_UpdateDateTime] DEFAULT (GETUTCDATE())
);
GO

CREATE TABLE [TrivialTest2](
    [MapRowID] [int] IDENTITY(1,1) NOT NULL,
    [ItemNumber] [int] NOT NULL,

    [ItemText] [nvarchar](max) NULL,
    
    [UpdateDateTime] [datetimeoffset](7) NOT NULL CONSTRAINT [DF_Trivial2_UpdateDateTime] DEFAULT ('01/01/1970')
);
GO

CREATE TABLE [TrivialTest3](
    [MapRowID] [int] IDENTITY(1,1) NOT NULL,
    [ItemNumber] [int] NOT NULL,

    [ItemText] [nvarchar](max) NULL,
    
    [UpdateDateTime] [datetimeoffset](7) NOT NULL CONSTRAINT [DF_Trivial3_UpdateDateTime] DEFAULT (GETDATE())
);
GO

IF EXISTS (SELECT * FROM sys.server_event_sessions WHERE name = 'MonitorWaits')
    DROP EVENT SESSION MonitorWaits ON SERVER;
GO

CREATE EVENT SESSION MonitorWaits ON SERVER
ADD EVENT sqlos.wait_info
    (WHERE sqlserver.database_name = 'InsertPerfTest')
ADD TARGET package0.asynchronous_file_target
    (SET FILENAME = N'C:\perftest\EE_WaitStats.xel',
    METADATAFILE = N'C:\perftest\EE_WaitStats.xem')
WITH (max_dispatch_latency = 1 seconds);
GO
Run Code Online (Sandbox Code Playgroud)

性能测试.sql:

SET NOCOUNT ON;
GO

PRINT '-------------------- SCRIPT START--------------------------------';
SELECT TEST = 'perftest.sql', SERVERNAME = CAST(@@SERVERNAME AS nvarchar(32)), [VERSION] = @@VERSION, DATABASENAME = CAST(DB_NAME() AS nvarchar(32));
TRUNCATE TABLE [TrivialTest];
TRUNCATE TABLE [TrivialTest2];
TRUNCATE TABLE [TrivialTest3];
SELECT RowsExistingTest = (SELECT COUNT(*) FROM [TrivialTest])
    , RowsExistingTest2 = (SELECT COUNT(*) FROM [TrivialTest2])
    , RowsExistingTest3 = (SELECT COUNT(*) FROM [TrivialTest3]);
GO

ALTER EVENT SESSION MonitorWaits ON SERVER STATE = START;
GO

DECLARE @ResultsMS AS TABLE(SetName nvarchar(50) NOT NULL, TimeMS int NOT NULL);
DECLARE @Start AS DATETIME;
DECLARE @End AS DATETIME;

SET @Start = GETDATE();

INSERT INTO TrivialTest (ItemNumber, [ItemText]) VALUES ('1', 'Put a huge lump of text here, not sure it really matters, but we want to get the rows size up to a size that''s similar to what we are seeing in the real world rule set.');
-- ... Another 998 rows here
INSERT INTO TrivialTest (ItemNumber, [ItemText]) VALUES ('1000', 'Put a huge lump of text here, not sure it really matters, but we want to get the rows size up to a size that''s similar to what we are seeing in the real world rule set.');

SET @End = GETDATE();
INSERT INTO @ResultsMS (SetName, TimeMS)
SELECT 'Set 1: GETUTCDATE', DATEDIFF(MILLISECOND, @Start, @End);

SET @Start = GETDATE();

INSERT INTO TrivialTest2 (ItemNumber, [ItemText]) VALUES ('1', 'Put a huge lump of text here, not sure it really matters, but we want to get the rows size up to a size that''s similar to what we are seeing in the real world rule set.');
-- ... Another 998 rows here
INSERT INTO TrivialTest2 (ItemNumber, [ItemText]) VALUES ('1000', 'Put a huge lump of text here, not sure it really matters, but we want to get the rows size up to a size that''s similar to what we are seeing in the real world rule set.');

SET @End = GETDATE();
INSERT INTO @ResultsMS (SetName, TimeMS)
SELECT 'Set 2: Literal', DATEDIFF(MILLISECOND, @Start, @End);

SET @Start = GETDATE();

INSERT INTO TrivialTest3 (ItemNumber, [ItemText]) VALUES ('1', 'Put a huge lump of text here, not sure it really matters, but we want to get the rows size up to a size that''s similar to what we are seeing in the real world rule set.');
-- ... Another 998 rows here
INSERT INTO TrivialTest3 (ItemNumber, [ItemText]) VALUES ('1000', 'Put a huge lump of text here, not sure it really matters, but we want to get the rows size up to a size that''s similar to what we are seeing in the real world rule set.');

SET @End = GETDATE();
INSERT INTO @ResultsMS (SetName, TimeMS)
SELECT 'Set 3: GETDATE', DATEDIFF(MILLISECOND, @Start, @End);

SELECT *
FROM @ResultsMS
ORDER BY SetName;

GO

ALTER EVENT SESSION MonitorWaits ON SERVER STATE = STOP;
GO

SELECT RowsExistingTest = (SELECT COUNT(*) FROM [TrivialTest])
    , RowsExistingTest2 = (SELECT COUNT(*) FROM [TrivialTest2])
    , RowsExistingTest3 = (SELECT COUNT(*) FROM [TrivialTest3]);
GO

-- https://www.sqlskills.com/blogs/paul/capturing-wait-stats-for-a-single-operation/
--Create intermediate temp table for raw event data
CREATE TABLE #RawEventData (
    Rowid  INT IDENTITY PRIMARY KEY,
    event_data XML);
 
GO

-- Read the file data into intermediate temp table
INSERT INTO #RawEventData (event_data)
SELECT
    CAST (event_data AS XML) AS event_data
FROM sys.fn_xe_file_target_read_file (
    'C:\perftest\EE_WaitStats*.xel',
    'C:\perftest\EE_WaitStats*.xem', null, null);
GO

SELECT
    waits.[Wait Type],
    COUNT (*) AS [Wait Count],
    SUM (waits.[Duration]) AS [Total Wait Time (ms)],
    SUM (waits.[Duration]) - SUM (waits.[Signal Duration]) AS [Total Resource Wait Time (ms)],
    SUM (waits.[Signal Duration]) AS [Total Signal Wait Time (ms)]
FROM
    (SELECT
        event_data.value ('(/event/@timestamp)[1]', 'DATETIME') AS [Time],
        event_data.value ('(/event/data[@name=''wait_type'']/text)[1]', 'VARCHAR(25)') AS [Wait Type],
        event_data.value ('(/event/data[@name=''opcode'']/text)[1]', 'VARCHAR(100)') AS [Op],
        event_data.value ('(/event/data[@name=''duration'']/value)[1]', 'BIGINT') AS [Duration],
        event_data.value ('(/event/data[@name=''signal_duration'']/value)[1]', 'BIGINT') AS [Signal Duration]
     FROM #RawEventData
    ) AS waits
WHERE waits.[op] = 'End'
GROUP BY waits.[Wait Type]
ORDER BY [Total Wait Time (ms)] DESC;
GO

-- Cleanup
DROP TABLE #RawEventData;
GO
Run Code Online (Sandbox Code Playgroud)

组合输出.txt:

-------------------- SCRIPT START--------------------------------
TEST      SERVERNAME                       VERSION                                                                                                                                                                                                                                                                                                      DATABASENAME                    
--------- -------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------------------------
setup.sql SQL2016DEV                       Microsoft SQL Server 2016 (SP2-GDR) (KB4532097) - 13.0.5102.14 (X64) 
    Dec 31 2019 22:39:35 
    Copyright (c) Microsoft Corporation
    Standard Edition (64-bit) on Windows Server 2012 R2 Standard 6.3 <X64> (Build 9600: ) (Hypervisor)
                                                                       master                          
Changed database context to 'InsertPerfTest'.
-------------------- SCRIPT START--------------------------------
TEST         SERVERNAME                       VERSION                                                                                                                                                                                                                                                                                                      DATABASENAME                    
------------ -------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------------------------
perftest.sql SQL2016DEV                       Microsoft SQL Server 2016 (SP2-GDR) (KB4532097) - 13.0.5102.14 (X64) 
    Dec 31 2019 22:39:35 
    Copyright (c) Microsoft Corporation
    Standard Edition (64-bit) on Windows Server 2012 R2 Standard 6.3 <X64> (Build 9600: ) (Hypervisor)
                                                                       InsertPerfTest                  
RowsExistingTest RowsExistingTest2 RowsExistingTest3
---------------- ----------------- -----------------
               0                 0                 0
SetName                                            TimeMS     
-------------------------------------------------- -----------
Set 1: GETUTCDATE                                        31920
Set 2: Literal                                             877
Set 3: GETDATE                                           31890
RowsExistingTest RowsExistingTest2 RowsExistingTest3
---------------- ----------------- -----------------
            1000              1000              1000
Wait Type                 Wait Count  Total Wait Time (ms) Total Resource Wait Time (ms) Total Signal Wait Time (ms)
------------------------- ----------- -------------------- ----------------------------- ---------------------------
WRITELOG                         3001                 1276                          1274                           2
SOS_SCHEDULER_YIELD              2066                   61                             2                          59
PAGEIOLATCH_SH                      1                    2                             2                           0
PAGELATCH_SH                        2                    0                             0                           0
NETWORK_IO                         38                    0                             0                           0
XE_BUFFERMGR_ALLPROCESSED           1                    0                             0                           0
PAGELATCH_EX                        2                    0                             0                           0
-------------------- SCRIPT START--------------------------------
TEST      SERVERNAME                       VERSION                                                                                                                                                                                                                                                                                                      DATABASENAME                    
--------- -------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------------------------
setup.sql SQL2016DEV                       Microsoft SQL Server 2016 (SP2-GDR) (KB4532097) - 13.0.5102.14 (X64) 
    Dec 31 2019 22:39:35 
    Copyright (c) Microsoft Corporation
    Standard Edition (64-bit) on Windows Server 2012 R2 Standard 6.3 <X64> (Build 9600: ) (Hypervisor)
                                                                       master                          
Changed database context to 'InsertPerfTest'.
-------------------- SCRIPT START--------------------------------
TEST             SERVERNAME                       VERSION                                                                                                                                                                                                                                                                                                      DATABASENAME                    
---------------- -------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------------------------
perftestloop.sql SQL2016DEV                       Microsoft SQL Server 2016 (SP2-GDR) (KB4532097) - 13.0.5102.14 (X64) 
    Dec 31 2019 22:39:35 
    Copyright (c) Microsoft Corporation
    Standard Edition (64-bit) on Windows Server 2012 R2 Standard 6.3 <X64> (Build 9600: ) (Hypervisor)
                                                                       InsertPerfTest                  
RowsExistingTest RowsExistingTest2 RowsExistingTest3
---------------- ----------------- -----------------
               0                 0                 0
SetName                                            TimeMS     
-------------------------------------------------- -----------
Set 1: GETUTCDATE                                          940
Set 2: Literal                                             796
Set 3: GETDATE                                            1030
RowsExistingTest RowsExistingTest2 RowsExistingTest3
---------------- ----------------- -----------------
            1000              1000              1000
Wait Type                 Wait Count  Total Wait Time (ms) Total Resource Wait Time (ms) Total Signal Wait Time (ms)
------------------------- ----------- -------------------- ----------------------------- ---------------------------
WRITELOG                         6002                 1798                          1792                           6
SOS_SCHEDULER_YIELD              2069                   61                             2                          59
PAGEIOLATCH_SH                      2                    2                             2                           0
XE_BUFFERMGR_ALLPROCESSED           2                    1                             1                           0
PAGELATCH_EX                        4                    0                             0                           0
PAGELATCH_SH                        4                    0                             0                           0
NETWORK_IO                         38                    0                             0                           0
Run Code Online (Sandbox Code Playgroud)

Fra*_*ani -4

SOS_SCHEDULER_YIELD表示您\xe2\x80\x99 已发生争用,并且查询花费了大量时间等待访问 CPU。

\n
    \n
  • 检查最大DOP:MAXDOP 是查询并行时将使用的最大内核数。如果设置 4,该查询将仅占用 4 个核心。如果您设置 0 所有核心都将被占用,因此您将经历等待和阻塞
  • \n
  • 检查并行成本阈值:并行成本阈值确定何时应并行化查询。不幸的是,微软默认设置为 5,这是一个可以追溯到 90 年代的旧默认值。如果您将该值保持在如此低的水平,则大多数查询将很快并行,如果您将 MAXDOP 设置为 0,那么所有核心都将被占用,您将经历等待和阻塞。将该值设置为 25 或 50,就像对您的查询说:“嘿,您,查询...您在我看来没那么重,呆在那里,不要平行”。这样,只有繁重的查询才会并行,此时 MAXDOP 将按照您在上一点中指定的确切速率进入您的方程式...
  • \n
\n

另一个测试:将 2016 年降级compatibility level到 2012 年,看看执行是否需要几秒钟。它可能与 2016 年使用的基数有关。

\n