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

Scanninger af allokeringsordre

Når en eksekveringsplan inkluderer en scanning af en b-tree indeksstruktur, kan kunne vælge mellem to fysiske adgangsstrategier, når planen eksekveres:

  1. Følg indeksets b-træstruktur; eller
  2. find sider ved hjælp af interne sidetildelingsoplysninger.

Hvor et valg er tilgængeligt, træffer storage-motoren køretidsbeslutningen for hver udførelse. En plangenkompilering er ikke kræves for, at den ændrer mening.

B-træstrategien starter ved roden af ​​træet, falder ned til en yderste kant af bladniveauet (afhængigt af om scanningen er fremad eller bagud), og følger derefter sidelinks på bladniveau, indtil den anden ende af indekset nås . Tildelingsstrategien bruger Index Allocation Map (IAM) strukturer til at lokalisere databasesider, der er allokeret til indekset. Hver IAM-side kortlægger allokeringer til et 4 GB-interval i en enkelt fysisk databasefil, så scanning af IAM-kæder, der er knyttet til et indeks, har en tendens til at få adgang til indekssider i fysisk filrækkefølge (i det mindste så vidt SQL Server kan se).

De vigtigste forskelle mellem de to strategier er:

  1. En b-tree-scanning kan levere rækker til forespørgselsprocessoren i indeksnøglerækkefølge; en IAM-drevet scanning kan ikke;
  2. en b-tree-scanning er muligvis ikke i stand til at udstede store I/O-anmodninger, hvis logisk sammenhængende indekssider ikke også er fysisk sammenhængende (f.eks. som følge af sideopdeling i indekset).

En b-træ scanning er altid tilgængelig for et indeks. Betingelserne, der ofte nævnes for, at allokeringsordrescanninger er tilgængelige, er:

  1. Forespørgselsplanen skal tillade en uordnet scanning af indekset;
  2. indekset skal være på mindst 64 sider. og,
  3. enten en TABLOCK eller NOLOCK tip skal angives.

Den første betingelse betyder blot, at forespørgselsoptimeringsværktøjet skal have markeret scanningen med Ordered:False ejendom. Markering af scanningen Ordered:False betyder, at korrekte resultater fra udførelsesplanen ikke kræver scanningen for at returnere rækker i indeksnøglerækkefølge (selvom den kan gøre det, hvis det er praktisk eller på anden måde nødvendigt).

Den anden betingelse (størrelse) gælder kun for SQL Server 2005 og nyere. Det afspejler, at der er en vis opstartsomkostning ved at udføre en IAM-drevet scanning, så der skal være et minimum antal sider for, at de potentielle besparelser kan tilbagebetale den oprindelige investering. "64 sider" refererer til værdien af ​​data_sider for IN_ROW_DATA kun tildelingsenhed, som rapporteret i sys.allocation_units.

Selvfølgelig kan der kun være et udbytte fra en tildelingsordrescanning, hvis de muligvis større read-ahead overvejelser faktisk kommer i spil, men SQL Server overvejer i øjeblikket ikke denne faktor. Især tager den ikke højde for, hvor meget af indekset der i øjeblikket er i hukommelsen, og det er heller ikke ligeglad med, hvor fragmenteret indekset er.

Den tredje betingelse er sandsynligvis den mindst fuldstændige beskrivelse på listen. Tip er faktisk ikke påkrævet , selvom de kan bruges til at opfylde de reelle krav:Dataene skal garanteret ikke ændres under scanningen, eller (mere kontroversielt) skal vi indikere, at vi er ligeglade om potentielt unøjagtige resultater ved at udføre scanningen på læst uforpligtende isolationsniveau.

Selv med disse præciseringer er listen over betingelser for en tildelingsbeordret scanning stadig ikke komplet. Der er en række vigtige forbehold og undtagelser, som vi vil komme ind på om kort tid.

Demo

Følgende forespørgsel bruger AdventureWorks-eksempeldatabasen:

CHECKPOINT;
DBCC DROPCLEANBUFFERS;
GO
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P;

Bemærk, at Person-tabellen indeholder 3.869 sider. Planen efter udførelse (faktisk) er som følger (vist i SQL Sentry Plan Explorer):

Med hensyn til tildelings-ordre-scanningskravene har vi indtil videre:

  • Planen har den påkrævede Ordered:False ejendom; og,
  • tabellen har mere end 64 sider; men,
  • vi har ikke gjort noget for at sikre, at dataene ikke kan ændre sig under scanningen. Hvis vi antager, at vores session bruger standarden læse-committed isolationsniveau, udføres scanningen ikke ved læst ukommitteret isolationsniveau enten.

Som en konsekvens ville vi forvente, at denne scanning udføres ved at scanne b-træet i stedet for at være IAM-drevet. Forespørgselsresultaterne indikerer, at dette sandsynligvis er sandt:

Rækkerne returneres i Clustered Index nøglerækkefølge (efter BusinessEntityID ). Jeg bør klart sige, at denne resultatrækkefølge er absolut ikke garanteret , og bør ikke stoles på. Bestilte resultater er kun garanteret af en passende ORDER BY på øverste niveau klausul.

Ikke desto mindre er den observerede outputrækkefølge indicier for, at scanningen blev udført denne gang ved at følge den klyngede indeks b-træstruktur. Hvis der er behov for mere bevis, kan vi vedhæfte en debugger og se på kodestien SQL Server udfører under scanningen:

Opkaldsstakken viser tydeligt scanningen efter b-træet.

Tilføjelse af et bordlåstip

Vi ændrer nu forespørgslen til at inkludere et tabellåstip:

CHECKPOINT;
DBCC DROPCLEANBUFFERS;
 
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P
    WITH (TABLOCK);

På standardlåsnings-læse-forpligtet isolationsniveau forhindrer den delte tabel-niveaulås enhver mulig samtidig modifikation af dataene. Med alle tre forudsætninger for IAM-drevne scanninger opfyldt, ville vi nu forvente, at SQL Server bruger en allokeringsordre-scanning. Udførelsesplanen er den samme som før, så jeg vil ikke gentage den, men forespørgselsresultaterne ser bestemt anderledes ud:

Resultaterne er tilsyneladende stadig sorteret efter BusinessEntityID , men udgangspunktet (10866) er anderledes. Faktisk, hvis vi ruller ned i resultaterne, støder vi snart på sektioner, der er mere åbenlyst ude af nøgleorden:

Den delvise bestilling skyldes, at allokeringsordrescanningen behandler en hel indeksside ad gangen. Resultaterne inden for en side tilfældigvis returneres sorteret efter indeksnøglen, men rækkefølgen af ​​de scannede sider er nu anderledes. Igen skal jeg understrege, at resultaterne kan se anderledes ud for dig:der er ingen garanti for outputrækkefølge, selv inden for en side, uden en ORDER BY på øverste niveau. på den oprindelige forespørgsel.

Til sammenligning med opkaldsstakken vist tidligere, er dette en staksporing opnået, mens SQL Server behandlede forespørgslen med TABLOCK tip:

Træder lidt videre gennem udførelsen:

Det er klart, at SQL Server udfører en allokeringsordret scanning, når tabellåsen er angivet. Det er en skam, at der ikke er nogen indikation i en post-udførelsesplan af, hvilken type scanning der blev brugt under kørsel. Som en påmindelse vælges typen af ​​scanning af lagringsmotoren og kan ændres mellem udførelser uden en plangenkompilering.

Andre måder at opfylde den tredje betingelse

Jeg sagde før, at for at få en IAM-drevet scanning, skal vi sikre, at dataene ikke kan ændre sig under scanningen, mens den er i gang, eller vi er nødt til at køre forespørgslen på læse-uforpligtet isolationsniveau. Vi har set, at et bordlås-tip om låsning af læseforpligtet isolation er tilstrækkeligt til at opfylde det første af disse krav, og det er nemt at vise, at brug af en NOLOCK/READUNCOMMITTED tip muliggør også en allokeringsordrescanning med demoforespørgslen.

Faktisk er der mange måder at opfylde den tredje betingelse, herunder:

  • Ændring af indekset til kun at tillade tabellåse;
  • gør databasen skrivebeskyttet (så data er garanteret ikke ændres); eller,
  • ændring af sessionen isolationsniveau til READ UNCOMMITTED .

Der er dog meget mere interessante variationer over dette tema, som betyder, at vi er nødt til at ændre de tre tidligere nævnte betingelser...

Isolationsniveauer for rækkeversionering

Aktiver read committed snapshot isolation (RCSI) på AdventureWorks-databasen, og kør testen med TABLOCK hint igen (ved læst forpligtet isolation):

ALTER DATABASE AdventureWorks2012
SET READ_COMMITTED_SNAPSHOT ON
    WITH ROLLBACK IMMEDIATE;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
CHECKPOINT;
DBCC DROPCLEANBUFFERS;
GO
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P
    WITH (TABLOCK);
GO
ALTER DATABASE AdventureWorks2012
SET READ_COMMITTED_SNAPSHOT OFF
    WITH ROLLBACK IMMEDIATE;

Med RCSI aktiv, en indeksordnet scanning bruges med TABLOCK , ikke den allokeringsordre-scanning, vi så lige før. Årsagen er TABLOCK tip angiver en delt lås på tabelniveau, men med RCSI aktiveret ingen delte låse er taget. Uden den delte tabellås har vi ikke opfyldt kravet om at forhindre samtidige ændringer af dataene, mens scanningen er i gang, så en allokeringsbestemt scanning kan ikke bruges.

Det er dog muligt at opnå en allokeringsbestemt scanning, når RCSI er aktiveret. En måde er at bruge en TABLOCKX tip (for en eksklusiv tabel på tabelniveau lås) i stedet for TABLOCK . Vi kunne også beholde TABLOCK tip og tilføj endnu en som READCOMMITTEDLOCK , eller REPEATABLE READ eller SERIALIZABLE … og så videre. Alle disse virker ved at forhindre muligheden for samtidige ændringer ved at tage en delt bordlås på bekostning af at miste fordelene ved RCSI . Vi kan også stadig opnå en allokeringsordre-scanning ved hjælp af en NOLOCK eller READUNCOMMITTED tip, selvfølgelig.

Situationen under snapshot isolation (SI) ligner meget RCSI og er ikke udforsket i detaljer af pladshensyn.

TABLESAMPLE udfører altid* en allokeringsordrescanning

TABLESAMPLE klausulen er en interessant undtagelse fra mange af de ting, vi har diskuteret indtil nu.

Angivelse af en TABLESAMPLE klausul altid* resulterer i en allokeringsordrescanning, selv under RCSI eller SI, og endda uden hints. For at være klar over det, scanningen af ​​allokeringsordre, der er resultatet af brugen af ​​TABLESAMPLE bevarer RCSI/SI-semantik – scanningen bruger rækkeversioner, og læsning blokerer ikke for skrivning (og omvendt).

En anden overraskelse er, at TABLESAMPLE udfører altid* en IAM-drevet scanning selvom tabellen har færre end 64 sider . Dette giver mening, fordi dokumentationen i det mindste antyder, at SYSTEM prøveudtagningsmetoden bruger IAM-strukturen (så der er intet andet valg end at lave en allokeringsordrescanning):

SYSTEM Er en implementeringsafhængig prøveudtagningsmetode specificeret af ISO-standarder. I SQL Server er dette den eneste tilgængelige prøvemetode og anvendes som standard. SYSTEM anvender en sidebaseret stikprøvemetode, hvor et tilfældigt sæt sider fra tabellen vælges til prøven, og alle rækkerne på disse sider returneres som prøveundermængden.

* En undtagelse opstår, hvis ROWS eller PERCENT specifikation i TABLESAMPLE klausul betyder 100 % af tabellen. Angivelse af flere ROWS end metadataene angiver i øjeblikket er i tabellen, vil heller ikke virke. Brug af TABLESAMPLE SYSTEM (100 PERCENT) eller tilsvarende vil ikke fremtvinge en allokeringsordre-scanning.

CHECKPOINT;
DBCC DROPCLEANBUFFERS;
GO
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P
    TABLESAMPLE SYSTEM (50 ROWS)
    REPEATABLE (12345678)
    --WITH (TABLOCK);

Resultater:

Effekten af ​​TOP og SET ROWCOUNT

Kort sagt, ingen af ​​disse har nogen indflydelse på beslutningen om at bruge en tildelingsordre-scanning eller ej. Dette kan virke overraskende i tilfælde, hvor det er "indlysende", at færre end 64 sider vil blive scannet.

For eksempel bruger følgende forespørgsler begge en IAM-drevet scanning til at returnere 5 rækker fra en scanning:

SELECT TOP (5)
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P WITH (TABLOCK)
 
SET ROWCOUNT 5;
 
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P WITH (TABLOCK)
 
SET ROWCOUNT 0;

Resultaterne er de samme for begge:

Det betyder, at TOP og SET ROWCOUNT forespørgsler måske pådrage sig omkostningerne ved opsætning af en tildelings-ordre scanning, selvom færre end 64 sider er scannet. Som afhjælpning kunne mere komplekse TOP-forespørgsler med selektive prædikater skubbet ind i scanningen stadig drage fordel af en allokeringsordre-scanning. Hvis scanningen skal behandle 10.000 sider for at finde de første 5 rækker, der matcher, kan en tildelingsordrescanning stadig være en gevinst.

Forhindring af alle* allokeringsordrescanninger i hele instansen

Dette er ikke noget, du sandsynligvis ville gøre med vilje, men der er en serverindstilling, der forhindrer scanninger af allokeringsordre for alle* brugerforespørgsler i alle databaser.

Hvor usandsynligt det kan virke, er den pågældende indstilling konfigurationsmuligheden for markørtærskelserveren, som har følgende beskrivelse i Books Online:

Markørtærskelindstillingen angiver antallet af rækker i markørsættet, hvor markørtastesæt genereres asynkront. Når markører genererer et nøglesæt for et resultatsæt, estimerer forespørgselsoptimeringsværktøjet antallet af rækker, der returneres for det resultatsæt. Hvis forespørgselsoptimeringsværktøjet estimerer, at antallet af returnerede rækker er større end denne tærskel, genereres markøren asynkront, hvilket giver brugeren mulighed for at hente rækker fra markøren, mens markøren fortsætter med at blive udfyldt. Ellers genereres markøren synkront, og forespørgslen venter, indtil alle rækker er returneret.

Hvis cursor threshold indstillingen er sat til noget andet end –1 (standard), vil der ikke forekomme scanninger af allokeringsrækkefølge for brugerforespørgsler i nogen database på SQL Server-instansen.

Med andre ord, hvis asynkron markørpopulation er aktiveret, scanner ingen IAM-drevet for dig.

* Undtagelsen er (ikke-100%) TABLESAMPLE forespørgsler. De interne forespørgsler genereret af systemet til statistikoprettelse og statistikopdateringer fortsætter også med at bruge allokeringsordrede scanninger.

CHECKPOINT;
DBCC DROPCLEANBUFFERS;
GO
 
-- WARNING! Disables allocation-order scans instance-wide
EXECUTE sys.sp_configure 
    @configname = 'cursor threshold',
    @configvalue = 5000;
 
RECONFIGURE WITH OVERRIDE;
GO
 
-- Would normally result in an allocation-order scan
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P
    WITH (READUNCOMMITTED);
GO
 
-- Reset to default allocation-order scans
EXECUTE sys.sp_configure 
    @configname = 'cursor threshold',
    @configvalue = -1;
 
RECONFIGURE WITH OVERRIDE;

Resultater (ingen allokeringsordrescanning):

Man kan kun gætte på, at asynkron markørpopulation af en eller anden grund ikke fungerer godt med allokeringsordre-scanninger. Det er helt uventet, at denne begrænsning vil påvirke alle brugerforespørgsler uden markør dog også. Måske er det for svært for SQL Server at opdage, om en forespørgsel kører som en del af en eksternt udstedt API-markør? Hvem ved.

Det ville være rart, hvis denne bivirkning blev officielt dokumenteret et sted, selvom det er svært at vide præcis, hvor det skal gå hen i Books Online. Jeg spekulerer på, hvor mange produktionssystemer derude, der ikke bruger allokeringsordrescanninger på grund af dette? Måske ikke mange, men man ved aldrig.

For at afslutte tingene, er her et resumé. En tildelingsbestemt scanning er tilgængelig, hvis:

  1. Serverindstillingen cursor threshold er indstillet til –1 (standardindstillingen); og
  2. Forespørgselsplanens scanningsoperator har Ordered:False ejendom; og
  3. det samlede antal data_sider af IN_ROW_DATA tildelingsenheder er mindst 64; og
  4. enten:
    1. SQL Server har en acceptabel garanti for, at samtidige ændringer er umulige; eller,
    2. scanningen kører på læst uforpligtende isolationsniveau.

Uanset alt ovenstående, en scanning med en TABLESAMPLE klausul bruger altid allokeringsordrede scanninger (med den ene tekniske undtagelse, der er noteret i hovedteksten).


  1. INSERT SELECT-sætning i Oracle 11G

  2. Hent poster, hvor json kolonnenøglen er null

  3. Hvordan indsætter man arabiske tegn i SQL-databasen?

  4. Oracle Kombiner flere kolonner til én