SQL Server bestaat al meer dan 30 jaar en ik werk al bijna net zo lang met SQL Server. Kalen behandelt scans in deel één van SQL Server Internals:Problematic Operators.
Ik heb in de loop der jaren (en decennia!) en versies van dit ongelooflijke product veel veranderingen gezien. In deze berichten zal ik met u delen hoe ik kijk naar enkele van de functies of aspecten van SQL Server, soms samen met een beetje historisch perspectief.
Het afstemmen van uw SQL Server-query's is een van de beste dingen die u kunt doen voor betere prestaties en optimalisatie van SQL Server-diagnose. Maar tuning is een enorm onderwerp! Om precies te weten hoe u op de best mogelijke manier kunt afstemmen, is niet alleen een grondige kennis van uw gegevens en uw werklast vereist, maar ook kennis van hoe SQL Server de keuzes voor de uitvoering van plannen maakt. Dus, wat kunt u doen als u geen expert bent op het gebied van SQL Server Internals? Een ding dat u kunt doen, is vertrouwen op mensen die experts zijn, evenals op tools die zijn geschreven door experts. Tools zoals Quest Spotlight Cloud Tuning Pack kunnen u enkele geweldige suggesties geven om op weg te gaan naar betere queryprestaties. Natuurlijk kent geen enkele externe tool uw gegevens en alle details van al uw workloads, dus het wordt altijd aanbevolen om elke suggestie die u besluit te implementeren grondig te testen.
In deze berichten over problematische operators ga ik ervan uit dat je enige basiskennis hebt van SQL Server-indexstructuren. Hier is wat informatie die nuttig zal zijn:
- Een tabel zonder een geclusterde index wordt een heap genoemd en heeft geen volgorde. Er is geen eerste rij of laatste rij. Een hoop is gewoon een aantal rijen in willekeurige volgorde.
- Het bladniveau van een geclusterde index is de tabel zelf. (Het is geen kopie van de tabel, het IS de tabel.) De rijen van de index zijn logisch geordend op de kolom die is gedefinieerd als de geclusterde indexsleutel.
- Het bladniveau van een niet-geclusterde index bevat een indexrij voor elke rij in de tabel. De rijen bevatten de niet-geclusterde sleutelkolommen en zijn logisch geordend in de volgorde waarin de sleutels zijn opgegeven. Naast de sleutelkolommen bevatten de niet-geclusterde indexrijen een 'bladwijzer' die verwijst naar de rij waarnaar wordt verwezen in de tabel. De bladwijzer kan twee vormen aannemen:
- Als de tabel een geclusterde index heeft, is de bladwijzer de geclusterde indexsleutel. (Als de geclusterde indexsleutel deel uitmaakt van de niet-geclusterde indexsleutel, wordt deze niet gedupliceerd.)
- Als de tabel een hoop is, is de bladwijzer een Rij-ID, of RID, die de fysieke locatie van de rij aangeeft. De locatie wordt meestal gespecificeerd als FileNum:PageNum:RowNum .
De eigen hulpprogramma's van SQL Server bieden meerdere manieren om het uitvoeringsplan voor query's te bekijken dat de optimizer heeft besloten te gebruiken voor een bepaalde query. Met de toevoeging van Quest Spotlight Tuning Pack kun je nog meer informatie krijgen over je plannen.
De volgende code maakt kopieën van twee tabellen in de AdventureWorks database (ik gebruik AdventureWorks2016 , maar je zou een andere versie kunnen gebruiken).
USE AdventureWorks2016;
GO
DROP TABLE IF EXISTS SalesHeader;
GO
SELECT *
INTO SalesHeader
FROM Sales.SalesOrderHeader;
GO
DROP TABLE IF EXISTS SalesDetail;
GO
SELECT * INTO SalesDetail
FROM Sales.SalesOrderDetail;
GO
Voer nu een query uit die de twee tabellen samenvoegt, na het inschakelen van "Include Actual Execution Plan"
SELECT h.SalesOrderID, OrderDate, ProductID, UnitPrice, OrderQty
FROM SalesHeader h JOIN SalesDetail d
ON h.SalesOrderID = d.SalesOrderID
WHERE SalesOrderDetailID < 100;
GO
Quest Spotlight Tuning Pack meldt een probleem met de zoekopdracht, dus u kunt op "Analyse bekijken" klikken en de optie "Uitvoeringsplan" kiezen. Je zou het volgende moeten zien:
Tafelscans begrijpen
Ten eerste wil ik eropuit gaan en zeggen dat er geen planoperator is die altijd slecht is! Waarom zou de optimizer het aan uw queryplan toevoegen als het slecht was? Het kan erop wijzen dat er ruimte is voor verbetering in uw gegevens- of indexstructuren, maar op zich is het niet slecht.
In het bovenstaande voorbeeld lijkt het Tuning Pack de tafelscans in de schijnwerpers te zetten, wat aangeeft dat ze problematisch kunnen zijn. Maar het is niet altijd waar dat tabelscans problematisch zijn. Een veel slechtere situatie zou zijn om een niet-geclusterde indexzoekopdracht te gebruiken voor een query die toegang heeft tot elke rij in de tabel. Voor deze specifieke vraag ben ik het ermee eens dat de scan misschien geen goede zaak is, omdat we slechts geïnteresseerd zijn in een paar rijen in de SalesDetail tabel (99 van de 121.317 rijen, of minder dan een tiende van een procent.)
We kunnen dus kijken naar de suggesties in het deelvenster Analyse voor het bouwen van indexen. De suggestie voor de SalesDetail tabel is om een niet-geclusterde index te bouwen op de SalesOrderID column (de kolom in de JOIN-component) en INCLUDE elke andere kolom in de tabel die door de query wordt geretourneerd. De suggestie voor de SalesHeader tabel is een niet-geclusterde index op de SalesOrderDetailId kolom, de kolom in de WHERE-component, en INCLUDE de OrderDate kolom, de enige andere kolom die uit deze tabel wordt geretourneerd.
Wat als onze vraag iets anders was? Wat als ik deze query had uitgevoerd met SELECT * in plaats van een specifieke kolomlijst. Als u het probeert en naar de aanbevelingen kijkt, stelt het voor om INCLUDE te gebruiken voor elke kolom in de tabel, behalve voor de enkele sleutelkolom. Hoewel een dergelijke index ervoor kan zorgen dat deze specifieke query iets sneller wordt uitgevoerd, kan het andere query's vertragen, met name uw UPDATE-query's. Deze index is in feite slechts een kopie van de tabel, omdat het bladniveau van de index elke afzonderlijke kolom in de tabel zal bevatten. Als je dit soort aanbevelingen ziet, waarbij een index wordt voorgesteld die alle kolommen in de tabel bevat, raad ik je zeker aan een stapje terug te doen en deze niet blindelings te maken.
Het afstemmen van query's voor uw SQL-serverdiagnostiek omvat niet alleen het beheren van indexen, maar ook het beheren van de query's zelf. Voor deze specifieke query is het misschien beter de query te herschrijven om NIET de SELECT * te gebruiken om elke rij in de tabel te retourneren. Het retourneren van slechts een kleine subset van de kolommen zou voldoende kunnen zijn, en dan zou een veel smallere index voldoende zijn, zoals in het eerste voorbeeld.
Zou een van deze indexen eigenlijk een goede index zijn om te maken? De smallere index zal over het algemeen kleiner zijn en minder worden beïnvloed door updates van de gegevens. Een index op alle kolommen is als een tweede kopie van de tabel, gesorteerd in een andere volgorde dan de tabel zelf. Er zijn situaties waarin het nuttig kan zijn om een 'tweede exemplaar' van de tabel in een andere volgorde te hebben, maar er zal veel overhead zijn voor bewerkingen voor het wijzigen van gegevens. De enige manier om het zeker te weten is om de aanbevelingen uit te proberen op een testsysteem met een representatieve werklast. Alleen jij kent je gegevens en je vragen, dus probeer het en zie het!
Indexscans begrijpen
Zoals ik hierboven al zei, zijn tabelscans niet altijd een slechte zaak. Maar hoe zit het met indexscans? Omdat een geclusterd indexbladniveau de tabel zelf is, is een geclusterde indexscan hetzelfde als een tabelscan! als een tabelscan slecht is, is een geclusterde indexscan net zo slecht. Maar het is niet altijd slecht. Nogmaals, je moet het op je systeem testen.
De aanbevelingen van de SQL Server Engine die Quest Spotlight Tuning Pack laat zien, suggereert nooit een geclusterde index. het kan een niet-geclusterd voorstel suggereren dat elke kolom in de tabel bevat (zoals eerder vermeld), wat slechts een duplicaat is van de tabel. Het uitzoeken van de beste kolom of kolommen voor uw geclusterde index is een groot onderwerp op zich, dus daar ga ik hier niet op in.
Wat is zoeken? Een zoekbewerking in een plan betekent dat SQL Server de geordende gegevens in de indexstructuur gebruikt om een rij, een reeks rijen of het begin- en/of eindpunt in een reeks rijen te vinden. Over het algemeen is het gebruik van een niet-geclusterde indexzoekactie een volkomen redelijke operatie als u slechts een heel klein percentage rijen uit een tabel retourneert. Maar zoeken is geen goede keuze voor een zoekopdracht die VEEL rijen uit een tabel retourneert. Hoeveel is VEEL? Er is geen eenvoudig antwoord, maar als uw zoekopdracht meer dan een paar procent van de rijen retourneert, moet u ervoor zorgen dat u de indexsuggesties grondig test. Soms is een tabelscan, of geclusterde indexscan, beter dan een indexzoekopdracht. (Zie voor zo'n voorbeeld mijn blogbericht hier).
Tools zoals Quest Spotlight Tuning Pack kan u geweldige suggesties geven om aan de slag te gaan met uw afstemmingsreis met SQL Server-diagnose, maar hoe meer u weet over hoe SQL Server-indexen en de SQL Server-optimizer werken, hoe beter u die suggesties voor uw query's en uw gegevens, en mogelijk zelfs zelf met suggesties komen.
In de volgende berichten in deze serie zal ik je vertellen over andere problematische operators die kunnen voorkomen in je queryplannen, dus kom snel terug!