We geven deze week IEPTO2 in Dublin (en als Ierland niet op je lijst staat van plaatsen om te zien in dit leven, moet je het toevoegen ... het is hier fantastisch) en vandaag heb ik de module Query Plan Analysis afgerond. Een ding dat ik behandel zijn interessante dingen die je in het zoekplan kunt vinden, bijvoorbeeld:
- NoJoinPredicate (2005 en hoger)
- ColumnsWithNoStatistics (2005 en hoger)
- UnmatchedIndexes (2008 en hoger)
- PlanAffectingConvert (2012 en hoger)
Deze kenmerken zijn handig om op te letten wanneer u naar een enkel plan of een reeks plannen kijkt terwijl u aan het afstemmen bent. Maar als je wat proactiever wilt zijn, kun je beginnen met het ontginnen van de plancache en ze daar zoeken. Hiervoor moet natuurlijk wel wat XQuery worden geschreven, aangezien de plannen XML zijn (ga voor details over het showplan-schema naar:http://schemas.microsoft.com/sqlserver/2004/07/showplan/). Ik hou niet van XML, hoewel niet omdat ik het niet geprobeerd heb, en toen een van de aanwezigen vroeg of je via Extended Events vragen kon vastleggen met het NoJoinPredicate-attribuut, dacht ik:"Wat een geweldig idee, ik zal .”
En ja hoor, daar is een evenement voor. Er is een evenement voor alle vier de hierboven genoemde:
- missing_join_predicaat
- missing_column_statistics
- unmatched_filtered_indexes
- plan_affecting_convert
Leuk. Het instellen van deze in een Extended Events-sessie is vrij eenvoudig. In dit geval zou ik aanraden om het event_file-doel te gebruiken, omdat je waarschijnlijk de gebeurtenissessie start en deze een tijdje laat lopen voordat je teruggaat en de uitvoer bekijkt. Ik heb een paar acties toegevoegd, in de hoop dat deze gebeurtenissen niet worden afgevuurd dat vaak, dus we voegen hier niet te veel overhead toe. Ik heb sql_text toegevoegd, ook al is het geen actie waar je echt op moet vertrouwen. Jonathan heeft dit eerder besproken, maar sql_text geeft je alleen de invoerbuffer, dus je krijgt misschien niet het volledige verhaal voor de vraag. Om die reden heb ik ook plan_handle toegevoegd. Het voorbehoud is dat, afhankelijk van wanneer je naar het plan gaat zoeken, het zich mogelijk niet langer in de plancache bevindt.
-- 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
Zodra de gebeurtenissessie actief is, kunnen we deze gebeurtenissen genereren met de onderstaande voorbeeldcode. Merk op dat deze code uitgaat van een nieuwe installatie van AdventureWorks2014. Als u er geen heeft, wordt de gebeurtenis missing_column_statistics mogelijk niet geactiveerd als u wordt opgevraagd in de kolom [HireDate] in [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
OPMERKING:NADAT u klaar bent met het ophalen van plannen uit de cache, kunt u de instructie ALTER uitvoeren om de optie voor het automatisch maken van statistieken in te schakelen. Als u dit op dit punt doet, wordt de plancache gewist en moet u helemaal opnieuw beginnen met testen. (En wacht ook tot je klaar bent om de index te laten vallen.)
ALTER DATABASE [AdventureWorks2014] SET AUTO_CREATE_STATISTICS ON; GO DROP INDEX [NCI_SalesOrderHeader] ON [Sales].[SalesOrderHeader]; GO
Aangezien ik de gebeurtenissessie heb gestopt, open ik het uitvoerbestand in SSMS om te zien wat we hebben vastgelegd:
Uitvoer van uitgebreide evenementen
Voor onze eerste query met een ontbrekend join-predikaat, hebben we één gebeurtenis die wordt weergegeven en ik kan de tekst voor de query in het veld sql_text zien. Wat ik echter echt wil, is ook naar het plan kijken, zodat ik de plan_handle kan nemen en sys.dm_exec_query_plan kan controleren:
SELECT query_plan FROM sys.dm_exec_query_plan (0x06000700E2200333405DD12C0000000001000000000000000000000000000000000000000000000000000000);
En dat openen in SQL Sentry Plan Explorer:
Ontbrekend lidmaatschapspredicaat
Het plan heeft een visuele indicator van het ontbrekende join-predikaat in de geneste lus (de rode X), en als ik erover zweef, zie ik de waarschuwing (en het staat in de XML voor het plan). Uitstekend... Ik kan nu met mijn ontwikkelaars gaan praten over het herschrijven van deze query.
De volgende gebeurtenis is voor een ontbrekende kolomstatistiek. Ik heb deze situatie volledig geforceerd door AUTO_CREATE_STATISTICS uit te schakelen voor de AdventureWorks2014-database. Ik raad dit op geen enkele manier, vorm of vorm aan. Deze optie is standaard ingeschakeld en ik raad aan deze altijd ingeschakeld te laten. Uitschakelen is echter de gemakkelijkste manier om deze gebeurtenis te genereren. Ik heb opnieuw de vraag in het veld sql_text, maar ik zal de plan_handle opnieuw gebruiken om het plan op te halen:
SELECT query_plan FROM sys.dm_exec_query_plan (0x060007004448323810921C360000000001000000000000000000000000000000000000000000000000000000);
Ontbrekende statistiek
En we hebben opnieuw een visuele aanwijzing (de gele driehoek met het uitroepteken) om aan te geven dat er een probleem is met het plan, en opnieuw staat het in de XML. Vanaf hier zou ik eerst controleren of AUTO_CREATE_STATISTICS is uitgeschakeld, en zo niet, dan zou ik beginnen met het uitvoeren van de query in Management Studio om te zien of ik de waarschuwing opnieuw kan maken (en de statistieken kan dwingen om te maken).
Nu zijn de overige evenementen een beetje interessanter.
U zult zien dat we drie unmatched_filtered_indexes-gebeurtenissen hebben. Ik moet nog bepalen waarom, maar ik werk eraan en zal in de reacties posten of/wanneer ik het voor elkaar krijg. Voor nu is het voldoende dat ik de gebeurtenis heb, en binnen de gebeurtenis kunnen we ook objectinformatie zien, zodat ik de betreffende index ken:
NCI_SalesOrderHeader-index waarnaar wordt verwezen door ontbrekende indexgebeurtenis
En ik kan opnieuw de plan_handle gebruiken om het queryplan te vinden:
Ongeëvenaarde index
Deze keer zie ik de waarschuwing in de SELECT-operator, dus ik weet dat er iets is dat ik verder moet onderzoeken. In dit geval heb je opties om de optimizer de gefilterde index te laten gebruiken wanneer je parameters gebruikt, en ik raad aan om Aarons post te lezen voor meer informatie over het gebruik van gefilterde indexen.
Ten slotte hebben we negen evenementen voor plan_affecting_convert. Wat maakt het uit? Ik ben deze nog steeds aan het uitzoeken, maar ik heb de Track Causality-optie voor mijn gebeurtenissessie (tijdens het testen) gebruikt om te bevestigen dat alle gebeurtenissen deel uitmaken van dezelfde taak (dat zijn ze). Als je naar het expressie-element in de uitvoer kijkt, zie je dat het enigszins verandert (net als compile_time), en dit komt naar voren als je naar de details van de waarschuwing kijkt in Plan Explorer van SQL Sentry (zie tweede screenshot hieronder). Binnen de gebeurtenisuitvoer, doet het expressie-element vertel ons om welke kolom het gaat, wat een begin is, maar lang niet genoeg informatie, dus nogmaals, we moeten het plan gaan halen:
SELECT query_plan FROM sys.dm_exec_query_plan (0x0600070023747010E09E1C360000000001000000000000000000000000000000000000000000000000000000);
Plan met invloed op conversie
Conversiedetail van het plan
We zien opnieuw onze vriend, de gele driehoek, in de SELECT-operator, en binnen de XML kunnen we het PlanAffectingConvert-attribuut vinden. Dit kenmerk is toegevoegd in het SQL Server 2012 showplan-schema, dus als u een eerdere versie gebruikt, ziet u dit niet in het plan. Het oplossen van deze waarschuwing kan wat meer werk vergen - u moet begrijpen waar u een niet-overeenkomend gegevenstype hebt en waarom, en dan beginnen met het wijzigen van de code of het schema ... beide kunnen op weerstand stuiten. Jonathan heeft een post die impliciete conversie in meer detail bespreekt, wat een goede plek is om te beginnen als je nog niet eerder met conversieproblemen hebt gewerkt.
Samenvatting
De uitgebreide evenementenbibliotheek met gebeurtenissen blijft groeien, en een ding om te overwegen bij het oplossen van problemen in SQL Server is of u de informatie die u zoekt op een andere manier kunt krijgen. Misschien omdat het makkelijker is (ik geef zeker de voorkeur aan XE boven XML!), Of omdat het efficiënter is, of je meer details geeft. Of u nu proactief op zoek bent naar queryproblemen in uw omgeving, of reageert op een probleem dat iemand heeft gemeld, maar u problemen ondervindt om het te vinden, uitgebreide gebeurtenissen zijn een haalbare optie om te overwegen, vooral omdat er meer nieuwe functies aan SQL Server worden toegevoegd.