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

Улавяне на предупреждения за план за изпълнение с помощта на разширени събития

Тази седмица преподаваме IEPTO2 в Дъблин (и ако Ирландия не е в списъка ви с места, които трябва да видите през този живот, трябва да го добавите... тук е фантастично) и днес завърших модула за анализ на плана на заявки. Едно нещо, което обхващам, са интересни неща, които можете да намерите в плана на заявката, например:

  • NoJoinPredicate (2005 и по-нови версии)
  • ColumnsWithNoStatistics (2005 и по-нови)
  • UnmatchedIndexes (2008 и по-нови)
  • PlanAffectingConvert (2012 и по-нови версии)

Тези атрибути е добре да търсите, когато разглеждате един план или набор от планове, докато настройвате. Но ако искате да сте малко по-активни, можете да започнете да копаете кеша на плана и да ги търсите там. Разбира се, за да направите това, е необходимо да напишете малко XQuery, тъй като плановете са XML (за подробности относно схемата на showplan вижте:http://schemas.microsoft.com/sqlserver/2004/07/showplan/). Не обичам XML, макар че не поради липса на опит, и когато един от присъстващите попита дали можете да уловите заявки, които имат атрибута NoJoinPredicate чрез разширени събития, си помислих:„Каква страхотна идея, ще трябва да проверя .”

Разбира се, има събитие за това. Има събитие и за четирите, които изброих по-горе:

  • missing_join_predicate
  • missing_column_statistics
  • unmatched_filtered_indexes
  • plan_affecting_convert

Хубаво. Настройването им в сесия с разширени събития е доста лесно. В този случай бих препоръчал да използвате целта event_file, тъй като вероятно ще стартирате сесията на събитието и ще я оставите да работи за малко, преди да се върнете и да прегледате изхода. Включих няколко действия с надеждата, че тези събития не се задействат това често, така че не добавяме твърде много допълнителни разходи тук. Включих sql_text, въпреки че това не е действие, на което наистина трябва да разчитате. Джонатан е обсъждал това преди, но sql_text просто ви дава входния буфер, така че може да не получите пълната история за заявката. Поради тази причина включих и plan_handle. Предупреждението е, че в зависимост от това кога търсите плана, той може вече да не е в кеша на плана.

-- Remove event session if it exists
IF EXISTS (SELECT 1 FROM [sys].[server_event_sessions]
WHERE [name] = 'InterestingPlanEvents')
BEGIN
  DROP EVENT SESSION [InterestingPlanEvents] ON SERVER
END
GO
 
-- Define event session
CREATE EVENT SESSION [InterestingPlanEvents]
ON SERVER
ADD EVENT sqlserver.missing_column_statistics
(
  ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text)
  WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))
    AND [sqlserver].[database_id]>(4))
),
ADD EVENT sqlserver.missing_join_predicate
(
  ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text)
  WHERE ([sqlserver].[is_system]=(0) AND [sqlserver].[database_id]>(4))
),
ADD EVENT sqlserver.plan_affecting_convert
(
  ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text)
  WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))
    AND [sqlserver].[database_id]>(4))
),
ADD EVENT sqlserver.unmatched_filtered_indexes
(
  ACTION(sqlserver.plan_handle,sqlserver.sql_text)
  WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))
    AND [sqlserver].[database_id]>(4))
)
ADD TARGET package0.event_file
(
  SET filename=N'C:\temp\InterestingPlanEvents' /* change location if appropriate */
)
WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,
TRACK_CAUSALITY=ON,STARTUP_STATE=OFF)
GO
 
-- Start the event session
ALTER EVENT SESSION [InterestingPlanEvents] ON SERVER STATE=START;
GO

След като стартираме и стартираме сесията на събитието, можем да генерираме тези събития с примерния код по-долу. Имайте предвид, че този код предполага нова инсталация на AdventureWorks2014. Ако нямате такъв, може да не видите събитието missing_column_statistics да се задейства, ако сте запитали колоната [HireDate] в [HumanResources].[Employee].

-- These queries assume a FRESH restore of AdventureWorks2014
ALTER DATABASE [AdventureWorks2014] SET AUTO_CREATE_STATISTICS OFF;
GO
 
USE [AdventureWorks2014];
GO
 
CREATE INDEX [NCI_SalesOrderHeader] ON [Sales].[SalesOrderHeader] (
[PurchaseOrderNumber], [CustomerID], [TotalDue], [DueDate]
)
WHERE [SubTotal] > 10000.00;
GO
 
/*
No join predicate
NOTE: We clear procedure here because the event ONLY fires for the *initial* compilation
*/
DBCC FREEPROCCACHE; /* Not for production use */
 
SELECT [h].[SalesOrderID], [d].[SalesOrderDetailID], [h].[CustomerID]
FROM [Sales].[SalesOrderDetail] [d],
[Sales].[SalesOrderHeader] [h]
WHERE [d].[ProductID] = 897;
GO
 
-- Columns with no statistics
SELECT [BusinessEntityID], [NationalIDNumber], [JobTitle], [HireDate], [ModifiedDate]
FROM [HumanResources].[Employee]
WHERE [HireDate] >= '2013-01-01';
GO
 
-- Unmatched Index
DECLARE @Total MONEY = 10000.00;
 
SELECT [PurchaseOrderNumber], [CustomerID], [TotalDue], [DueDate]
FROM [Sales].[SalesOrderHeader]
WHERE [SubTotal] > @Total;
GO
 
-- Plan Affecting Convert
SELECT [BusinessEntityID], [NationalIDNumber], [JobTitle], [HireDate], [ModifiedDate]
FROM [HumanResources].[Employee]
WHERE [NationalIDNumber] = 345106466;
GO
 
ALTER EVENT SESSION [InterestingPlanEvents]
ON SERVER
STATE=STOP;
GO
 
DROP EVENT SESSION [InterestingPlanEvents]
ON SERVER;
GO

ЗАБЕЛЕЖКА:СЛЕД като приключите с изтеглянето на планове от кеша, можете да изпълните оператора ALTER, за да активирате опцията за автоматично създаване на статистически данни. Правейки това в този момент ще изчистите кеша на плана и ще трябва да започнете с тестването си отначало. (И също така изчакайте, докато приключите, за да пуснете индекса.)

ALTER DATABASE [AdventureWorks2014] SET AUTO_CREATE_STATISTICS ON;
GO
 
DROP INDEX [NCI_SalesOrderHeader] ON [Sales].[SalesOrderHeader];
GO

Тъй като спрях сесията на събитието, ще отворя изходния файл в SSMS, за да видя какво сме заснели:

Изход от разширени събития

За първата ни заявка с липсващ предикат за присъединяване имаме едно събитие, което се показва и мога да видя текста за заявката в полето sql_text. Но това, което наистина искам, е да погледна и плана, за да мога да взема plan_handle и да проверя sys.dm_exec_query_plan:

SELECT query_plan FROM sys.dm_exec_query_plan
(0x06000700E2200333405DD12C0000000001000000000000000000000000000000000000000000000000000000);

И отваряне в SQL Sentry Plan Explorer:

Липсващ предикат за присъединяване

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

Следващото събитие е за липсваща статистика на колона. Напълно принудих тази ситуация, като изключих AUTO_CREATE_STATISTICS за базата данни AdventureWorks2014. Не препоръчвам това по никакъв начин, форма или форма. Тази опция е активирана по подразбиране и препоръчвам винаги да я оставяте активирана. Изключването му обаче е най-лесният начин за генериране на това събитие. Отново имам заявката в полето sql_text, но отново ще използвам plan_handle, за да изтегля плана:

SELECT query_plan FROM sys.dm_exec_query_plan
(0x060007004448323810921C360000000001000000000000000000000000000000000000000000000000000000);

Липсваща статистика

И отново имаме визуален знак (жълтия триъгълник с удивителния знак), за да покажем, че има проблем с плана, и отново е в XML. Оттук първо ще проверя дали AUTO_CREATE_STATISTICS е деактивиран и ако не, ще започна да изпълнявам заявката в Management Studio, за да видя дали мога да създам отново предупреждението (и да принудя статистическите данни да се създават).

Сега останалите събития са малко по-интересни.

Ще забележите, че имаме три събития unmatched_filtered_indexes. Все още трябва да определя защо, но работя върху това и ще публикувам в коментарите, ако/когато го подредя. Засега е достатъчно, че имам събитието и в рамките на събитието можем да видим и информация за обекта, така че да знам въпросния индекс:

Индекс NCI_SalesOrderHeader, посочен от липсващо събитие в индекса

И мога отново да взема plan_handle, за да намеря плана на заявката:

Несъответстващ индекс

Този път виждам предупреждението в оператора SELECT, така че знам, че има нещо, което трябва да проуча допълнително. В този случай имате опции да накарате оптимизатора да използва филтрирания индекс, когато използвате параметри, и препоръчвам да прегледате публикацията на Aaron за повече информация относно използването на филтрирани индекси.

И накрая, имаме девет събития за plan_affecting_convert. Какво по дяволите? Все още измислям това, но използвах опцията Проследяване на причинно-следствената връзка за моята сесия на събитието (при тестване), за да потвърдя, че всички събития са част от една и съща задача (те са). Ако погледнете елемента на израза в изхода, виждате, че той се променя леко (както и compile_time) и това се появява, когато погледнете подробностите на предупреждението в Plan Explorer на SQL Sentry (вижте втората екранна снимка по-долу). В изхода на събитието елементът на израза does кажете ни за каква колона става дума, което е начало, но не е почти достатъчно информация, така че отново трябва да отидем да вземем плана:

SELECT query_plan FROM sys.dm_exec_query_plan
(0x0600070023747010E09E1C360000000001000000000000000000000000000000000000000000000000000000);

Планът, засягащ преобразуването

Подробности за преобразуването от плана

Отново виждаме нашия приятел, жълтия триъгълник, в оператора SELECT и в XML можем да намерим атрибута PlanAffectingConvert. Този атрибут е добавен в схемата за показване на SQL Server 2012, така че ако използвате по-ранна версия, няма да видите това в плана. Разрешаването на това предупреждение може да изисква малко повече работа – трябва да разберете къде имате несъответствие на типа данни и защо, и след това или започнете да променяте кода или схемата... и двете могат да бъдат срещнати със съпротива. Джонатан има публикация, която обсъжда по-подробно имплицитно преобразуване, което е добро място да започнете, ако не сте работили с проблеми с преобразуването преди това.

Резюме

Библиотеката от събития с разширени събития продължава да нараства и едно нещо, което трябва да имате предвид при отстраняване на неизправности в SQL Server е дали можете да получите информацията, която търсите, по друг начин. Може би защото е по-лесно (сигурно предпочитам XE пред XML!), или защото е по-ефективно, или ви дава повече подробности. Независимо дали търсите проактивно проблеми със заявките във вашата среда, или реагирате на проблем, за който някой е докладвал, но имате проблеми с намирането му, разширените събития са жизнеспособна опция, която да обмислите, особено след като към SQL Server се добавят още нови функции.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Използване на таблици за конфигурация за дефиниране на действителния работен поток

  2. Съвети за по-добър дизайн на база данни

  3. Извличане на XMLA за анализиране на структурата на куба

  4. Влияние на фрагментацията върху плановете за изпълнение

  5. Как да преброите броя на редовете в таблица в SQL