In Monday’snewsletter I discussed an email question I’d been sent and promised to blog about it.
The question was “how can latch waits for ACCESS_METHODS_DATASET_PARENT occur when the instance MAXDOP is set to 1?” This shouldn’t happen, as that latch is only used for coordination between threads performing a parallel table/index scan. However, just because the instance MAXDOP is set to 1, that doesn’t mean no parallelism can occur.
Anyone can override the instance MAXDOP setting using a query hint.
There are two ways to prevent this:
Set the instance cost threshold for parallelism setting to be extremely high, so no serial plans can be deemed expensive enough to run in parallel Make everyone run within a Resource Governor (Enterprise Edition only) workload group that has the MAX_DOP set to 1Or you can figure out where the query hint is being used and remove it.
In this post I’m going to show you a simple Extended Events session that will capture information about what’s causing CXPACKET waits (you can’t have ACCESS_METHOD_DATASET_PARENT latch waits without CXPACKET waits happening too) and then refine it to use a less expensive event.
First off I’ll set my MAXDOP to 1:
EXEC sp_configure 'show advanced options', 1;RECONFIGURE WITH OVERRIDE;
GO
EXEC sp_configure 'max degree of parallelism', 1;
RECONFIGURE WITH OVERRIDE;
GO
SELECT [name], [value_in_use] FROM sys.configurations WHERE [name] LIKE '%degree%';
GO Configuration option 'show advanced options' changed from 0 to 1. Run the RECONFIGURE statement to install.
Configuration option 'max degree of parallelism' changed from 0 to 1. Run the RECONFIGURE statement to install.
name value_in_use
-------------------------- -------------
max degree of parallelism 1
Then I’ll check for CXPACKET waits (usingmy waits query) after running the following query, that scans a 6.7 million row table (you can get the SalesDB database fromhere):
SELECT * FROM [SalesDB].[dbo].[Sales] WHERE [Quantity] > 10000;GO
No CXPACKET waits.
But if I add a MAXDOP query hint and then check for waits:
SELECT * FROM [SalesDB].[dbo].[Sales] WHERE [Quantity] > 10000 OPTION (MAXDOP 8);GO WaitType Wait_S Resource_S Signal_S WaitCount Percentage AvgWait_S AvgRes_S AvgSig_S Help/Info URL
------------------- ------- ---------- -------- --------- ---------- --------- -------- -------- ---------------------------------------------
LATCH_EX 0.20 0.16 0.05 93 79.61 0.0022 0.0017 0.0005 https://www.sqlskills.com/help/waits/LATCH_EX
CXPACKET 0.05 0.05 0.00 16 20.00 0.0032 0.0032 0.0000 https://www.sqlskills.com/help/waits/CXPACKET
And the instance MAXDOP was successfully overridden.
Now I’ll set up a simple Extended Events session to track down the offending code (based on the query fromhere). It’s very important that you query the sys.dm_xe_map_values DMV to find the correct number to use in the query for the CXPACKET wait, as these numbers often change from release to release, and even in Service Packs. For instance, CXPACKET was 191 in SQL Server 2014 RTM, but is 190 in the 2014 build I’m using.
Be very careful about running this in production, as the event will fire for *every* wait that occurs and so will likely affect your workload throughput, even though it’ll short-circuit if the wait isn’t CXPACKET . I’ll show you a better event to use lower down.
IF EXISTS (SELECT * FROM sys.server_event_sessions
WHERE [name] = N'InvestigateWaits')
DROP EVENT SESSION [InvestigateWaits] ON SERVER
GO
CREATE EVENT SESSION [InvestigateWaits] ON SERVER
ADD EVENT [sqlos].[wait_info]
(
ACTION (
sqlserver.client_hostname,
sqlserver.nt_username,
sqlserver.sql_text)
WHERE [wait_type] = 190 -- CXPACKET only
AND [opcode] = 1 -- Just the end wait events
)
ADD TARGET [package0].[ring_buffer]
WITH
(
MAX_MEMORY = 50 MB,
MAX_DISPATCH_LATENCY = 5 SECONDS)
GO
ALTER EVENT SESSION [InvestigateWaits] ON SERVER STATE = START;
GO
And then when I run the select statement again I canlook in the ring buffer and see theevents. I put in a DISTINCT to minimize the number of lines of output. The code is:
SELECTDISTINCT ([data1].[value] ('(./@timestamp)[1]', 'datetime')) AS [Time],
[data1].[value] ('(./action[@name="client_hostname"]/value)[1]', 'VARCHAR(MAX)') AS [Host],
[data1].[value] ('(./action[@name="nt_username"]/value)[1]', 'VARCHAR(MAX)') AS [User],
[data1].[value] ('(./action[@name="sql_text"]/value)[1]','VARCHAR(MAX)') AS [Statement]
FROM (
SELECT CONVERT (XML, [target_data]) AS data
FROM sys.dm_xe_session_targets [xst]
INNER JOIN sys.dm_xe_sessions [xs]
ON [xst].[event_session_address] = [xs].[address]
WHERE [xs].[name] = N'InvestigateWaits') AS t
CROSS APPLY data.nodes('//event') n (data1);
GO Time Host User Statement
----------------------- ---------- --------------- ---------------------------------------------------------------------------------
2017-02-16 00:20:16.937 APPLECROSS APPLECROSS\Paul SELECT * FROM [SalesDB].[dbo].[Sales] WHERE [Quantity] > 10000 OPTION (MAXDOP 8);
2017-02-16 00:20:16.987 APPLECROSS APPLECROSS\Paul SELECT * FROM [SalesDB].[dbo].[Sales] WHERE [Quantity] > 10000 OPTION (MAXDOP 8);
2017-02-16 00:20:16.937 APPLECROSS SELECT * FROM [SalesDB].[dbo].[Sales] WHERE [Quantity] > 10000 OPTION (MAXDOP 8);
Cool so that works, but the session will likely affect workload performance, as I mentioned above. A better event to use is degree_of_parallelism , which was introduced in SQL Server 2012 and only fires once per batch execution, rather than once for every wait that occurs.
The updated event session is:
CREATE EVENT SESSION [InvestigateWaits] ON SERVERADD EVENT [sqlserver].[degree_of_parallelism]
(
ACTION (
sqlserver.client_hostname,
sqlserver.nt_username,
sqlserver.sql_text)
WHERE [dop] > 0 -- parallel plans
)
ADD TARGET [package0].[ring_buffer]
WITH
(
MAX_MEMORY = 50 MB,
MAX_DISPATCH_LATENCY = 5 SECONDS)
GO
And the code to parse the XML, and sample output from my query is:
SELECT[data1].[value] ('(./@timestamp)[1]', 'datetime') AS [Time],
[data1].[value] ('(./data[@name="dop"]/value)[1]', 'INT') AS [DOP],
[data1].[value] ('(./action[@name="client_hostname"]/value)[1]', 'VARCHAR(MAX)') AS [Host],
[data1].[value] ('(./action[@name="nt_username"]/value)[1]', 'VARCHAR(MAX)') AS [User],
[data1].[value] ('(./action[@name="sql_text"]/value)[1]','VARCHAR(MAX)') AS [Statement]
FROM (
SELECT CONVERT (XML, [target_data]) AS data
FROM sys.dm_xe_session_targets [xst]
INNER JOIN sys.dm_xe_sessions [xs]
ON [xst].[event_session_address] = [xs].[address]
WHERE [xs].[name] = N'InvestigateWaits') AS t
CROSS APPLY data.nodes('//event') n (data1);
GO Time DOP Host User Statement
----------------------- --- ------






