Database
 sql >> база данни >  >> RDS >> Database

Повече за CXPACKET чака:изкривен паралелизъм

В предишната си публикация обсъдих изчакването на CXPACKET и начините за предотвратяване или ограничаване на паралелизъм. Обясних също как контролната нишка в паралелна операция винаги регистрира изчакване на CXPACKET и че понякога неконтролните нишки могат също да регистрират изчакване на CXPACKET. Това може да се случи, ако една от нишките е блокирана в очакване на ресурс (така че всички останали нишки завършват преди него и регистрира CXPACKET също чака), или ако оценките за кардиналност са неправилни. В тази публикация бих искал да разгледам последното.

Когато оценките за мощността са неправилни, на паралелните нишки, извършващи работата по заявката, се дава неравномерно количество работа за извършване. Типичният случай е, когато на една нишка се дава цялата работа или много повече работа от другите нишки. Това означава, че онези нишки, които завършват обработката на своите редове (ако дори са им дадени) преди най-бавната нишка, регистрират CXPACKET от момента, в който завършат, докато завърши най-бавната нишка. Този проблем може да доведе до привидна експлозия в изчакването на CXPACKET и обикновено се нарича изкривен паралелизъм , тъй като разпределението на работата между успоредните нишки е изкривено, не равномерно.

Имайте предвид, че в SQL Server 2016 SP2 и SQL Server 2017 RTM CU3 потребителските нишки вече не регистрират CXPACKET чакания. Те регистрират CXCONSUMER чакания, които са доброкачествени и могат да бъдат игнорирани. Това е да се намали броят на генерираните изчаквания на CXPACKET и е по-вероятно останалите да бъдат изпълними.

Пример за изкривен паралелизъм

Ще разгледам един измислен пример, за да покажа как да идентифицираме такива случаи.

Първо, ще създам сценарий, при който таблица има изключително неточни статистически данни, като ръчно задам броя на редовете и страниците в АКТУАЛИЗИРАНЕ НА СТАТИСТИКАТА изявление (не правете това в производството!):

USE [master];
GO
 
IF DB_ID (N'ExecutionMemory') IS NOT NULL
BEGIN
    ALTER DATABASE [ExecutionMemory] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE [ExecutionMemory];
END
GO
 
CREATE DATABASE [ExecutionMemory];
GO
USE [ExecutionMemory];
GO
 
CREATE TABLE dbo.[Test] (
    [RowID] INT IDENTITY,
    [ParentID] INT,
    [CurrentValue] NVARCHAR (100),
    CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED ([RowID]));
GO
 
INSERT INTO dbo.[Test] ([ParentID], [CurrentValue])
SELECT 
    CASE WHEN ([t1].[number] % 3 = 0)
        THEN [t1].[number] – [t1].[number] % 6
        ELSE [t1].[number] END, 
    'Test' + CAST ([t1].[number] % 2 AS VARCHAR(11))
FROM [master].[dbo].[spt_values] AS [t1]
WHERE [t1].[type] = 'P';
GO
 
UPDATE STATISTICS dbo.[Test] ([PK_Test]) WITH ROWCOUNT = 10000000, PAGECOUNT = 1000000;
GO

Така че моята таблица има само няколко хиляди реда в нея, но аз се фалшифицирах, че има 10 милиона реда.

Сега ще създам измислена заявка, за да избера първите 500 реда, които ще вървят успоредно, тъй като смята, че има милиони редове за сканиране.

USE [ExecutionMemory];
GO
 
SET NOCOUNT ON;
GO
 
DECLARE @CurrentValue NVARCHAR (100);
 
WHILE (1=1)
SELECT TOP (500) 
    @CurrentValue = [CurrentValue]
FROM dbo.[Test]
ORDER BY NEWID() DESC;
GO

И пуснете това да работи.

Преглед на CXPACKET чака

Сега мога да разгледам изчакванията на CXPACKET, които се случват, като използвам прост скрипт, за да разгледам sys.dm_os_waiting_tasks DMV:

SELECT
    [owt].[session_id],
    [owt].[exec_context_id],
    [owt].[wait_duration_ms],
    [owt].[wait_type],
    [owt].[blocking_session_id],
    [owt].[resource_description],
    [er].[database_id],
    [eqp].[query_plan]
FROM sys.dm_os_waiting_tasks [owt]
INNER JOIN sys.dm_exec_sessions [es] ON
    [owt].[session_id] = [es].[session_id]
INNER JOIN sys.dm_exec_requests [er] ON
    [es].[session_id] = [er].[session_id]
OUTER APPLY sys.dm_exec_sql_text ([er].[sql_handle]) [est]
OUTER APPLY sys.dm_exec_query_plan ([er].[plan_handle]) [eqp]
WHERE
    [es].[is_user_process] = 1
ORDER BY
    [owt].[session_id],
    [owt].[exec_context_id];

Ако изпълня това няколко пъти, в крайна сметка виждам някои резултати, показващи изкривен паралелизъм (отмахнах връзката на манипулатора на плана на заявката и намалих описанието на ресурса, за по-голяма яснота и забележете, че поставих кода, за да грабна SQL текста, ако искате това също):

session_id exec_context_id wait_duration_ms тип_чакане blocking_session_id описание_ресурс идентификатор на база_данни
56 0 1 CXPACKET NULL exchangeEvent 13
56 1 1 CXPACKET 56 exchangeEvent 13
56 3 1 CXPACKET 56 exchangeEvent 13
56 4 1 CXPACKET 56 exchangeEvent 13
56 5 1 CXPACKET 56 exchangeEvent 13
56 6 1 CXPACKET 56 exchangeEvent 13
56 7 1 CXPACKET 56 exchangeEvent 13

Резултати, показващи изкривен паралелизъм в действие

Контролната нишка е тази с exec_context_id зададено на 0. Другите паралелни нишки са тези с exec_context_id по-висока от 0 и всички те показват изчакване на CXPACKET освен едно (обърнете внимание, че exec_context_id = 2 липсва от списъка). Ще забележите, че всички те изброяват свои собствени session_id като този, който ги блокира, и това е правилно, защото всички нишки чакат друга нишка от своя собствен session_id да завърши. идентификаторът на базата данни е базата данни, в чийто контекст се изпълнява заявката, не непременно базата данни, където е проблемът, но обикновено е така, освен ако заявката не използва именуване от три части за изпълнение в друга база данни.

Преглед на проблема с оценката на кардиналността

С план_заявка колона в изхода на заявката (която премахнах за яснота), можете да щракнете върху нея, за да изведете графичния план и след това да щракнете с десния бутон и да изберете Преглед с SQL Sentry Plan Explorer. Това е показано по-долу:

Веднага виждам, че има проблем с оценката на кардиналността, тъй като действителните редове за сканирането на клъстериран индекс са само 2 048, в сравнение с 10 000 000 приблизителни (приблизителни) реда.

Ако превъртя, мога да видя разпределението на редовете в използваните паралелни нишки:

Ето, само една нишка вършеше някаква работа по време на паралелната част от плана – тази, която не се показваше в sys.dm_os_waiting_tasks изход по-горе.

В този случай поправката е да се актуализират статистическите данни за таблицата.

В моя измислен пример това няма да работи, тъй като не е имало никакви модификации в таблицата, така че ще стартирам отново скрипта за настройка, като пропускам АКТУАЛИЗИРАНЕ НА СТАТИСТИКАТА изявление.

След това планът на заявката става:

Там, където няма проблем с кардиналността и няма паралелизъм – проблемът е решен!

Резюме

Ако видите, че CXPACKET чака изчакване, лесно е да проверите за изкривен паралелизъм, като използвате метода, описан по-горе. Всички случаи, които съм виждал, се дължат на проблеми с оценката на кардиналността от един или друг вид и често това е просто случай на актуализиране на статистиката.

Що се отнася до общата статистика за чакане, можете да намерите повече информация за използването им за отстраняване на неизправности в производителността в:

  • Моята серия от публикации в блога на SQLskills, започваща със статистически данни за чакане или моля, кажете ми къде боли
  • Моята библиотека за типове чакане и класове на заключване тук
  • Моят онлайн курс за обучение на Pluralsight SQL Server:Отстраняване на проблеми с производителността с помощта на статистика за чакане
  • SQL Sentry

До следващия път, щастливо отстраняване на неизправности!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Изненади и предположения при представянето:GROUP BY срещу DISTINCT

  2. Призма отношения

  3. Топ 18 безплатни и широко използвани бази данни NoSQL с отворен код

  4. Оттеглени функции, които да извадите от кутията си с инструменти – част 2

  5. Свързване на Google BigQuery към софтуера IRI Voracity