sql >> Database teknologi >  >> RDS >> Sqlserver

SQL Server Internals:Problematiske Operatører Pt. I – Scanninger

SQL Server har eksisteret i over 30 år, og jeg har arbejdet med SQL Server i næsten lige så lang tid. Kalen dækker scanninger i første del af SQL Server Internals:Problematic Operators.

Jeg har set en masse ændringer gennem årene (og årtier!) og versioner af dette utrolige produkt. I disse indlæg vil jeg dele med dig, hvordan jeg ser på nogle af funktionerne eller aspekterne af SQL Server, nogle gange sammen med en smule historisk perspektiv.

Justering af dine SQL Server-forespørgsler er en af ​​de bedste ting, du kan gøre for bedre ydeevne og optimering af SQL-serverdiagnostik. Men tuning er et kæmpe emne! At vide præcis, hvordan man tuner på den bedst mulige måde, kræver ikke kun et grundigt kendskab til dine data og din arbejdsbyrde, men viden om, hvordan SQL Server faktisk træffer sine planudførelsesvalg. Så hvad kan du gøre, hvis du ikke er ekspert i SQL Server Internals? En ting, du kan gøre, er at stole på folk, der er eksperter, såvel som værktøjer skrevet af eksperter. Værktøjer som Quest Spotlight Cloud Tuning Pack kan give dig nogle gode forslag til at komme i gang på vejen mod bedre forespørgselsydeevne. Naturligvis kender intet eksternt værktøj dine data og alle detaljer om alle dine arbejdsbelastninger, så en grundig test af ethvert forslag, du beslutter dig for at implementere, anbefales altid.

I disse indlæg om problematiske operatører vil jeg antage, at du har en vis grundlæggende viden om SQL Server-indeksstrukturer. Her er nogle oplysninger, der vil være nyttige:

  • En tabel uden et klynget indeks kaldes en heap og har ingen rækkefølge. Der er ingen første række eller sidste række. En bunke er bare en flok rækker uden bestemt rækkefølge.
  • Løvniveauet for et klynget indeks er selve tabellen. (Det er ikke en kopi af tabellen, det ER tabellen.) Indeksets rækker er logisk ordnet efter den kolonne, der er defineret til at være den klyngede indeksnøgle.
  • Løvniveauet for et ikke-klynget indeks indeholder en indeksrække for hver række i tabellen. Rækkerne indeholder de ikke-klyngede nøglekolonner og er logisk ordnet i den rækkefølge, nøglerne er angivet. Ud over nøglekolonnerne indeholder de ikke-klyngede indeksrækker et 'bogmærke', der peger på den refererede række i tabellen. Bogmærket kan have en af ​​to former:
    1. Hvis tabellen har et klynget indeks, er bogmærket den klyngede indeksnøgle. (Hvis den klyngede indeksnøgle er en del af den ikke-klyngede indeksnøgle, bliver den ikke duplikeret.)
    2. Hvis tabellen er en bunke, er bogmærket et række-id eller RID, som angiver den fysiske placering af rækken. Placeringen er normalt angivet som FileNum:PageNum:RowNum .

SQL Servers egne værktøjer giver flere måder at se den forespørgselsudførelsesplan, som optimeringsværktøjet besluttede at bruge til en bestemt forespørgsel. Med tilføjelsen af ​​Quest Spotlight Tuning Pack kan du få endnu mere information om dine planer.

Følgende kode opretter kopier af to tabeller i AdventureWorks database (jeg bruger AdventureWorks2016 , men du kan bruge en anden version).

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

Udfør nu en forespørgsel, der forbinder de to tabeller, efter at du har slået "Inkluder faktisk udførelsesplan" til

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 vil rapportere et problem med forespørgslen, så du kan klikke på "Se analyse" og vælge muligheden "Udførelsesplan". Du bør se følgende:

Forstå tabelscanninger

Først vil jeg gå ud og sige, at der ikke er nogen planoperatør, der altid er dårlig! Hvorfor ville optimeringsværktøjet tilføje det til din forespørgselsplan, hvis det var dårligt? Det kan indikere, at der er plads til forbedringer i dine data eller indeksstrukturer, men i sig selv er det ikke dårligt.

I eksemplet ovenfor ser Tuning Pack ud til at sætte fokus på bordscanningerne, hvilket indikerer, at de kan være problematiske. Men det er ikke altid rigtigt, at bordscanninger er problematiske. En meget værre situation ville være at bruge en ikke-klynget indekssøgning til en forespørgsel, der får adgang til hver række i tabellen. For denne specifikke forespørgsel er jeg enig i, at scanningen måske ikke er en god ting, fordi vi kun er interesserede i nogle få rækker i SalgDetail tabel (99 ud af 121.317 rækker, eller mindre end en tiendedel af en procent).

Så vi kunne se på forslagene i Analyseruden til at bygge indekser. Forslaget til SalesDetail tabellen er at bygge et ikke-klynget indeks på SalesOrderID kolonne (kolonnen i JOIN-sætningen) og INKLUDERE hver anden kolonne i tabellen, der returneres af forespørgslen. Forslaget til SalesHeader tabel er et ikke-klynget indeks på SalesOrderDetailId kolonne, som er kolonnen i WHERE-sætningen, og INKLUSIVE OrderDate kolonne, som er den eneste anden kolonne, der returneres fra denne tabel.

Hvad hvis vores forespørgsel var lidt anderledes? Hvad hvis jeg havde kørt denne forespørgsel ved hjælp af SELECT * i stedet for en specifik kolonneliste. Hvis du prøver det og ser på anbefalingerne, foreslår det, at du bruger INCLUDE for hver kolonne i tabellen bortset fra den enkelte nøglekolonne. Selvom et sådant indeks kan få denne særlige forespørgsel til at køre en smule hurtigere, kan det ende med at bremse andre forespørgsler, især dine OPDATERINGsforespørgsler. Dette indeks er grundlæggende kun en kopi af tabellen, fordi indeksets bladniveau vil indeholde hver enkelt kolonne i tabellen. Hvis du ser anbefalinger som denne, der foreslår et indeks, der inkluderer alle kolonnerne i tabellen, anbefaler jeg bestemt at gå lidt tilbage og ikke blindt oprette det.

Forespørgselstuning til din SQL-serverdiagnostik involverer ikke kun styring af indekser, men også styring af selve forespørgslerne. For denne specifikke forespørgsel kan vi faktisk være bedre stillet til at omskrive forespørgslen til IKKE at bruge SELECT * til at returnere hver række i tabellen. At returnere kun en lille delmængde af kolonnerne kan være tilstrækkeligt, og så ville et meget smallere indeks være tilstrækkeligt, som i det første eksempel.

Ville et af disse indeks faktisk være et godt indeks at oprette? Det smallere indeks vil generelt være mindre og vil være mindre påvirket af opdateringer af dataene. Et indeks på alle kolonnerne er som en anden kopi af tabellen, sorteret i en anden rækkefølge end selve tabellen. Der er situationer, hvor det kan være nyttigt at have en 'anden kopi' af tabellen i en anden rækkefølge, men der vil være en masse overhead til dataændringsoperationer. Den eneste måde at vide det med sikkerhed ved at prøve anbefalingerne på et testsystem med en repræsentativ arbejdsbyrde. Kun du kender dine data og dine forespørgsler, så prøv det og se!

Forstå indeksscanninger

Som jeg nævnte ovenfor, er bordscanninger ikke altid en dårlig ting. Men hvad med indeksscanninger? Fordi et klynget indeksbladsniveau er selve tabellen, er en klynget indeksscanning det samme som en tabelscanning! hvis en tabelscanning er dårlig, er en klynget indeksscanning lige så dårlig. Men det er ikke altid dårligt. Igen skal du teste det på dit system.

Anbefalingerne fra SQL Server Engine, som Quest Spotlight Tuning Pack viser, foreslår du aldrig et klynget indeks. det kan foreslå en ikke-klynge, som inkluderer hver kolonne i tabellen (som tidligere nævnt), som blot er en duplikat af tabellen. At finde ud af den eller de bedste kolonner til dit grupperede indeks er et stort emne i sig selv, så det vil jeg ikke gå ind på her.

Hvad er en søgen? En søgeoperation i en plan betyder, at SQL Server bruger de ordnede data i indekstræet til at finde en række, et sæt rækker eller start- og/eller stoppunktet i en række rækker. Generelt er det en helt rimelig operation at bruge en ikke-klynget indekssøgning, hvis du kun returnerer en meget lille procentdel af rækker fra en tabel. Men en søgning er ikke et godt valg til en forespørgsel, der returnerer MASSER af rækker fra en tabel. Hvor mange er LOTS? Der er ikke noget enkelt svar, men hvis din forespørgsel returnerer mere end et par procent af rækkerne, bør du sørge for at teste indeksforslagene grundigt. Nogle gange er en tabelscanning eller klynget indeksscanning bedre end en indekssøgning. (For et sådant eksempel, se mit blogindlæg her).

Værktøjer såsom Quest Spotlight Tuning Pack kan give dig gode forslag til at komme i gang med din tuning-rejse med SQL-serverdiagnostik, men jo mere du ved om, hvordan SQL Server-indekser og SQL Server-optimering fungerer, jo bedre vil du være i stand til at evaluere disse forslag til dine forespørgsler og data, og måske endda komme med dine egne forslag.

I de følgende indlæg i denne serie vil jeg fortælle dig om andre problematiske operatører, der kan dukke op i dine forespørgselsplaner, så tjek snart tilbage!


  1. Kan ikke oprette forbindelse til postgres ved hjælp af jdbc i pyspark shell

  2. Er det muligt at bruge Full Text Search (FTS) med LINQ?

  3. SQL DEFAULT Begrænsning for at indsætte kolonne med en standardværdi til SQL Server-tabel

  4. mysql fuldtekstsøgning mislykkedes