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

Sådan starter parallelle planer op – del 1

Denne serie i fem dele tager et dybt dyk ned i den måde, parallelle planer for SQL Server-rækketilstande starter op. Denne første del dækker forældreopgavens (koordinatorens) rolle i udarbejdelsen af ​​planen for parallel udførelse. Det omfatter initialisering af hver operatør og tilføjelse af skjulte profiler for at indsamle data om runtime-ydeevne såsom faktisk rækkeantal og forløbet tid.

Opsætning

For at give et konkret grundlag for analysen vil vi følge, hvordan en bestemt parallel forespørgsel starter eksekveringen. Jeg brugte den offentlige Stack Overflow 2013 database (download detaljer). Den ønskede planform kan også opnås mod det mindre Stack Overflow 2010-datasæt, hvis det er mere bekvemt. Den kan downloades på samme link. Jeg tilføjede et ikke-klynget indeks:

CREATE NONCLUSTERED INDEX PP
ON dbo.Posts
(
    PostTypeId ASC,
    CreationDate ASC
);

Mit testmiljø er SQL Server 2019 CU9 på en bærbar computer med 8 kerner og 16 GB hukommelse allokeret til instansen. Kompatibilitetsniveau 150 bruges udelukkende. Jeg nævner disse detaljer for at hjælpe dig med at reproducere målplanen, hvis du ønsker det. De grundlæggende principper for parallel eksekvering i rækketilstand har ikke ændret sig siden SQL Server 2005, så følgende diskussion er bredt anvendelig.

Testforespørgslen returnerer det samlede antal spørgsmål og svar, grupperet efter måned og år:

WITH 
    MonthlyPosts AS 
    (
        SELECT 
            P.PostTypeId, 
            CA.TheYear, 
            CA.TheMonth, 
            Latest = MAX(P.CreationDate)
        FROM dbo.Posts AS P
        CROSS APPLY 
        (
            VALUES
            (
                YEAR(P.CreationDate), 
                MONTH(P.CreationDate)
            )
        ) AS CA (TheYear, TheMonth)
        GROUP BY 
            P.PostTypeId, 
            CA.TheYear, 
            CA.TheMonth
    )
SELECT 
    rn = ROW_NUMBER() OVER (
        ORDER BY Q.TheYear, Q.TheMonth),
    Q.TheYear, 
    Q.TheMonth, 
    LatestQuestion = Q.Latest,
    LatestAnswer = A.Latest
FROM MonthlyPosts AS Q
JOIN MonthlyPosts AS A
    ON A.TheYear = Q.TheYear
    AND A.TheMonth = Q.TheMonth
WHERE 
    Q.PostTypeId = 1
    AND A.PostTypeId = 2
ORDER BY 
    Q.TheYear,
    Q.TheMonth
OPTION 
(
    USE HINT ('DISALLOW_BATCH_MODE'),
    USE HINT ('FORCE_DEFAULT_CARDINALITY_ESTIMATION'),
    ORDER GROUP,
    MAXDOP 2
);

Jeg har brugt tip til at få en bestemt formrækketilstandsplan. Udførelse er begrænset til DOP 2 for at gøre nogle af de detaljer, der vises senere, mere kortfattede.

Den estimerede udførelsesplan er (klik for at forstørre):

Baggrund

Forespørgselsoptimeringsværktøjet producerer en enkelt kompileret plan for en batch. Hver erklæring i partiet er markeret til seriel eller parallel udførelse, afhængigt af berettigelse og anslåede omkostninger.

En parallel plan indeholder udvekslinger (parallelisme-operatorer). Udvekslinger kan forekomme i distribuerede streams , omopdelingsstrømme , eller saml streams form. Hver af disse udvekslingstyper bruger de samme underliggende komponenter, bare koblet anderledes op, med et forskelligt antal input og output. For mere baggrund om rækketilstand parallel udførelse se Parallelle udførelsesplaner – grene og tråde.

DOP-nedgradering

Graden af ​​parallelitet (DOP) for en parallel plan kan nedgraderes under kørsel, hvis det er nødvendigt. En parallel forespørgsel kan starte med at anmode om DOP 8, men blive gradvist nedgraderet til DOP 4, DOP 2 og endelig DOP 1 på grund af mangel på systemressourcer på det tidspunkt. Hvis du gerne vil se det i aktion, så se denne korte video af Erik Darling.

Kørsel af en parallel plan på en enkelt tråd kan også ske, når en cachelagret parallelplan genbruges af en session, der er begrænset til DOP 1 af en miljøindstilling (f.eks. affinitetsmaske eller ressourceregulator). Se Myte:SQL Server cacherer en seriel plan med hver parallel plan for detaljer.

Uanset årsagen, gør DOP-nedgradering af en cachelagret parallelplan ikke resultere i, at en ny serieplan kompileres. SQL Server genbruger den eksisterende parallelle plan ved at deaktivere udvekslingerne. Resultatet er en 'parallel' plan, der udføres på en enkelt tråd. Udvekslingerne vises stadig i planen, men de omgås under kørsel.

SQL Server kan ikke fremme en seriel plan til parallel eksekvering ved at tilføje udvekslinger under kørsel. Det ville kræve en ny kompilering.

Parallel planinitialisering

En parallel plan har alle de udvekslinger, der er nødvendige for at bruge ekstra arbejdstråde, men der er yderligere opsætningsarbejde nødvendig under kørsel, før parallel eksekvering kan begynde. Et oplagt eksempel er, at yderligere arbejdstråde skal allokeres til specifikke opgaver inden for planen, men der er meget mere i det end det.

Jeg vil starte på det punkt, hvor en parallel plan er blevet hentet fra planens cache. På dette tidspunkt eksisterer kun den originale tråd, der behandler den aktuelle anmodning. Denne tråd kaldes nogle gange "koordinatortråden" i parallelle planer, men jeg foretrækker udtrykkene "forældreopgave ” eller ”forældremedarbejder”. Der er ellers ikke noget særligt ved denne tråd; det er den samme tråd, som forbindelsen bruger til at behandle klientanmodninger og køre serielle planer til færdiggørelse.

For at understrege pointen om, at kun en enkelt tråd eksisterer lige nu vil jeg have dig til at visualisere planen på dette tidspunkt sådan her:

Jeg vil næsten udelukkende bruge skærmbilleder fra Sentry One Plan Explorer i dette indlæg, men kun til denne første visning vil jeg også vise SSMS-versionen:

I begge repræsentationer er den vigtigste forskel manglen på parallelitetsikoner på hver operatør, selvom udvekslingerne stadig er til stede. Kun den overordnede opgave eksisterer lige nu og kører på den originale forbindelsestråd. Ingen yderligere arbejdstråde er blevet reserveret, oprettet eller tildelt opgaver endnu. Hold ovenstående planrepræsentation for øje, mens vi går videre.

Oprettelse af den eksekverbare plan

Planen på dette tidspunkt er i det væsentlige kun en skabelon der kan bruges som grundlag for enhver fremtidig udførelse. For at gøre det klar til en specifik kørsel skal SQL Server udfylde kørselsværdier som den aktuelle bruger, transaktionskontekst, parameterværdier, id'er for alle objekter, der er oprettet under kørsel (f.eks. midlertidige tabeller og variabler) og så videre.

For en parallel plan skal SQL Server gøre en del ekstra forberedende arbejde for at få det interne maskineri til det punkt, hvor eksekveringen kan starte. Overordnet opgaves arbejdstråd er ansvarlig for at udføre næsten alt dette arbejde (og bestemt alt det arbejde, vi vil dække i del 1).

Processen med at transformere planskabelonen for en specifik kørsel er kendt som at skabe den eksekverbare plan . Det er nogle gange svært at holde terminologien ved lige, fordi termer ofte er overbelastede og forkert anvendt (selv af Microsoft), men jeg vil gøre mit bedste for at være så konsekvent som muligt.

Eksekveringskontekster

Du kan tænke på en udførelseskontekst som en planskabelon udfyldt med alle de specifikke runtime-oplysninger, der kræves af en bestemt tråd. Den eksekverbare plan for en føljeton statement består af en enkelt eksekveringskontekst, hvor en enkelt tråd kører hele planen.

En parallel eksekverbar plan indeholder en samling af udførelseskontekster :En til overordnet opgave og en pr. tråd i hver parallelgren. Hver yderligere parallel arbejdstråd kører sin del af den overordnede plan inden for sin egen udførelseskontekst. For eksempel har en parallel plan med tre grene, der kører ved DOP 8, (1 + (3 * 8)) =25 udførelseskontekster. Serielle eksekveringskontekster cachelagres til genbrug, men yderligere parallelle eksekveringskontekster er det ikke.

Den overordnede opgave eksisterer altid før eventuelle yderligere parallelle opgaver, så den er tildelt udførelseskontekst nul . De udførelseskontekster, der bruges af parallelle arbejdere, vil blive oprettet senere, efter at den overordnede kontekst er fuldt initialiseret. De yderligere kontekster er klonet fra forældrekonteksten og derefter tilpasset til deres specifikke opgave (dette er dækket i del 2).

Der er en række aktiviteter involveret i opstart af eksekveringskontekst nul. Det er upraktisk at forsøge at liste dem alle, men det vil være nyttigt at dække nogle af de vigtigste, der er relevante for vores testforespørgsel. Der vil stadig være for mange til en enkelt liste, så jeg deler dem op i (lidt vilkårlige) sektioner:

1. Initialisering af forældrekontekst

Når vi sender erklæringen til udførelse, initialiseres den overordnede opgaves kontekst (udførelseskontekst nul) med:

  • En reference til basistransaktionen (eksplicit, implicit eller auto-commit). Parallelle arbejdere kører undertransaktioner, men de er alle omfattet af basistransaktionen.
  • En liste over sætningsparametre og deres nuværende værdier.
  • Et primært hukommelsesobjekt (PMO) bruges til at administrere hukommelsesbevillinger og tildelinger.
  • Et linket kort af operatørerne (forespørgselsnoder) i den eksekverbare plan.
  • En fabrik til enhver påkrævet stor genstand (klat) håndtag.
  • Lås klasser for at holde styr på flere låse holdt i en periode under udførelsen. Ikke alle planer kræver låseklasser da fuldt streaming-operatører typisk låser og låser individuelle rækker op i rækkefølge.
  • Det estimerede hukommelsestilskud for forespørgslen.
  • Rækketilstandshukommelse giver feedback strukturer for hver operatør (SQL Server 2019).

Mange af disse ting vil blive brugt eller refereret til af parallelle opgaver senere, så de skal eksistere i forældreområdet først.

2. Overordnet kontekst metadata

De næste hovedopgaver, der udføres, er:

  • Tjekker den anslåede forespørgselsomkostning er inden for den grænse, der er angivet af konfigurationsmulighederne for forespørgselsregulatorens omkostningsgrænse.
  • Opdatering af indeksbrug poster – eksponeret gennem sys.dm_db_index_usage_stats .
  • Oprettelse af cachelagrede udtryksværdier (runtime-konstanter).
  • Oprettelse af en liste over profileringsoperatører bruges til at indsamle runtime-metrics som rækkeantal og timings, hvis dette er blevet anmodet om til den aktuelle udførelse. Selve profilerne er ikke oprettet endnu, kun listen.
  • Tag et øjebliksbillede af venter for session waits-funktionen afsløret via sys.dm_exec_session_wait_stats .

3. DOP og hukommelsesbevilling

Overordnet opgavekontekst nu:

  • Beregner runtime grad af parallelisme (DOP ). Dette er påvirket af antallet af ledige arbejdere (se "DOP-nedgradering" tidligere), hvor de kan placeres blandt noder, og en række sporingsflag.
  • Reserverer det nødvendige antal tråde. Dette trin er rent regnskab – selve trådene eksisterer muligvis ikke på dette tidspunkt. SQL Server holder styr på det maksimale antal tråde, det er tilladt at have. Reservering af tråde trækker fra dette tal. Når trådene er færdige med, øges det maksimale antal igen.
  • Indstiller timeout for hukommelsestildeling .
  • Beregner hukommelsestildelingen, inklusive hukommelse, der kræves til udvekslingsbuffere.
  • Erhverver hukommelsesbevillingen via den relevante ressource-semafor .
  • Opretter et administratorobjekt til at håndtere parallelle underprocesser . Forældreopgaven er processen på øverste niveau; yderligere opgaver er også kendt som underprocesser .

Selvom tråde er 'reserverede' på dette tidspunkt, kan SQL Server stadig støde på THREADPOOL venter senere, når den forsøger at bruge en af ​​de 'reserverede' tråde. Reservationen garanterer, at SQL Server vil forblive omkring dets konfigurerede maksimale antal tråde til enhver tid, men den fysiske tråd er muligvis ikke umiddelbart tilgængelig fra trådpuljen . Når det sker, skal en ny tråd startes af operativsystemet, hvilket kan tage lidt tid. For mere om det, se Unusual THREADPOOL Waits af Josh Darnell.

4. Opsætning af forespørgselsscanning

Rækketilstandsplaner udføres i iterativ mode, startende ved roden. Den plan, vi har i øjeblikket, kan endnu ikke udføres på denne måde. Det er stadig stort set en skabelon, selvom det allerede indeholder en del udførelsesspecifikke oplysninger.

SQL Server skal konvertere den aktuelle struktur til et træ af iteratorer , hver med metoder som Open , GetRow og Close . Iteratormetoderne er forbundet med deres børn via funktionspointere. For eksempel ved at kalde GetRow på roden kalder rekursivt GetRow på børneoperatører, indtil et bladniveau er nået, og en række begynder at 'boble' træet op. For en genopfriskning af detaljerne se Iteratorer, forespørgselsplaner og hvorfor de kører baglæns.

Slut på del 1

Vi har gjort gode fremskridt med at opsætte udførelseskonteksten for moderopgaven. I del 2 følger vi med, mens SQL Server konstruerer det forespørgselsscanningstræ, der er nødvendigt for iterativ udførelse.


  1. PostgreSQL-array af elementer, der hver er en fremmednøgle

  2. Mysql:Vælg alle data mellem to datoer

  3. Sådan opgraderes MySQL på CentOS

  4. Sådan fungerer Cosd() i PostgreSQL