sql >> Databasteknik >  >> RDS >> Database

Hur parallella planer startar – del 1

Denna femdelade serie tar en djupdykning i hur parallella planer för SQL Server-radläge startar. Denna första del tar upp rollen som föräldrauppgiften (koordinatorn) vid utarbetandet av planen för parallellt genomförande. Det inkluderar att initiera varje operatör och lägga till dolda profiler för att samla in körtidsprestandadata som faktisk radantal och förfluten tid.

Inställningar

För att ge ett konkret underlag för analysen kommer vi att följa hur en viss parallellfråga startar exekvering. Jag använde den offentliga Stack Overflow 2013 databas (nedladdningsinformation). Den önskade planformen kan också erhållas mot den mindre Stack Overflow 2010-datauppsättningen om det är bekvämare. Den kan laddas ner på samma länk. Jag lade till ett icke-klustrat index:

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

Min testmiljö är SQL Server 2019 CU9 på en bärbar dator med 8 kärnor och 16 GB minne tilldelat instansen. Kompatibilitetsnivå 150 används uteslutande. Jag nämner dessa detaljer för att hjälpa dig att reproducera målplanen om du vill. Grunderna för parallellkörning av radläge har inte förändrats sedan SQL Server 2005, så följande diskussion är allmänt tillämplig.

Testfrågan returnerar det totala antalet frågor och svar, grupperade efter månad och å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
);

Jag har använt tips för att få en plan för en viss formradsläge. Exekveringen är begränsad till DOP 2 för att göra några av detaljerna som visas senare mer kortfattade.

Den beräknade genomförandeplanen är (klicka för att förstora):

Bakgrund

Frågeoptimeraren producerar en enda kompilerad plan för en batch. Varje uttalande i partiet är markerat för seriell eller parallell exekvering, beroende på behörighet och beräknade kostnader.

En parallell plan innehåller utbyten (parallellismoperatorer). Utbyten kan visas i distributionsströmmar , ompartitionsströmmar , eller samla strömmar form. Var och en av dessa utbytestyper använder samma underliggande komponenter, bara kopplade på olika sätt, med olika antal ingångar och utgångar. För mer bakgrund om radläge parallellt exekvering, se Parallella exekveringsplaner – grenar och trådar.

DOP-nedgradering

Graden av parallellism (DOP) för en parallell plan kan vid behov nedgraderas vid körning. En parallell fråga kan börja med att begära DOP 8, men gradvis nedgraderas till DOP 4, DOP 2 och slutligen DOP 1 på grund av brist på systemresurser i det ögonblicket. Om du vill se det i aktion, se den här korta videon av Erik Darling.

Att köra en parallell plan på en enskild tråd kan också hända när en cachad parallellplan återanvänds av en session som är begränsad till DOP 1 av en miljöinställning (t.ex. affinitetsmask eller resursregulator). Se Myt:SQL Server cachar en serieplan med varje parallellplan för detaljer.

Oavsett orsak, inte gör DOP-nedgradering av en cachad parallell plan resultera i att en ny serieplan sammanställs. SQL Server återanvänder den befintliga parallella planen genom att inaktivera utbytena. Resultatet är en "parallell" plan som körs på en enda tråd. Utbytena visas fortfarande i planen, men de förbigås vid körning.

SQL Server kan inte främja en seriell plan till parallell exekvering genom att lägga till utbyten vid körning. Det skulle kräva en ny sammanställning.

Parallell planinitiering

En parallell plan har alla utbyten som behövs för att använda extra arbetstrådar, men det finns ytterligare installationsarbete behövs under körning innan parallell exekvering kan påbörjas. Ett uppenbart exempel är att ytterligare arbetstrådar måste allokeras till specifika uppgifter inom planen, men det finns mycket mer i det än så.

Jag ska börja vid den punkt där en parallell plan har hämtats från plancachen. Vid denna tidpunkt finns bara den ursprungliga tråden som behandlar den aktuella begäran. Den här tråden kallas ibland för "koordinatortråden" i parallella planer, men jag föredrar termerna "förälderuppgift ” eller ”föräldraarbetare”. Det är annars inget speciellt med den här tråden; det är samma tråd som anslutningen använder för att bearbeta klientförfrågningar och köra serieplaner till slut.

För att understryka poängen att bara en enda tråd existerar just nu vill jag att du ska visualisera planen vid denna tidpunkt så här:

Jag kommer att använda skärmdumpar från Sentry One Plan Explorer nästan uteslutande i det här inlägget, men endast för denna första visning kommer jag också att visa SSMS-versionen:

I båda representationerna är den viktigaste skillnaden avsaknaden av parallellitetsikoner på varje operatör, även om utbytena fortfarande är närvarande. Bara den överordnade uppgiften existerar just nu och körs på den ursprungliga anslutningstråden. Inga ytterligare arbetstrådar har reserverats, skapats eller tilldelats uppgifter ännu. Ha ovanstående planrepresentation framför sinnet när vi fortsätter.

Skapa den körbara planen

Planen vid denna tidpunkt är i huvudsak bara en mall som kan användas som grund för eventuella framtida utförande. För att göra den redo för en specifik körning måste SQL Server fylla i körtidsvärden som den aktuella användaren, transaktionskontext, parametervärden, ID för alla objekt som skapas vid körning (t.ex. temporära tabeller och variabler) och så vidare.

För en parallell plan behöver SQL Server göra en hel del ytterligare förberedande arbete för att få det interna maskineriet till den punkt där exekvering kan börja. Den överordnade uppgiftens arbetstråd är ansvarig för att utföra nästan allt detta arbete (och säkerligen allt arbete som vi kommer att täcka i del 1).

Processen att transformera planmallen för en specifik körning kallas att skapa den körbara planen . Det är ibland svårt att hålla terminologin rak, eftersom termer ofta överbelastas och används felaktigt (även av Microsoft), men jag ska göra mitt bästa för att vara så konsekvent som möjligt.

Körningssammanhang

Du kan tänka på en exekveringskontext som en planmall fylld med all specifik körtidsinformation som behövs av en viss tråd. Den körbara planen för en serie satsen består av en enda exekveringskontext, där en enda tråd kör hela planen.

En parallell den körbara planen innehåller en samling exekveringskontexter :En för den överordnade uppgiften och en per tråd i varje parallellgren. Varje ytterligare parallell arbetstråd driver sin del av den övergripande planen inom sitt eget exekveringssammanhang. Till exempel har en parallell plan med tre grenar som körs vid DOP 8 (1 + (3 * 8)) =25 exekveringskontexter. Seriella körningskontexter cachelagras för återanvändning, men ytterligare parallella körningskontexter är det inte.

Den överordnade uppgiften finns alltid före eventuella ytterligare parallella uppgifter, så den tilldelas exekveringskontext noll . Exekveringskontexterna som används av parallella arbetare kommer att skapas senare, efter att den överordnade kontexten är helt initialiserad. De ytterligare sammanhangen är klonade från det överordnade sammanhanget, sedan anpassade för deras specifika uppgift (detta tas upp i del 2).

Det finns ett antal aktiviteter involverade i att starta upp exekveringskontext noll. Det är opraktiskt att försöka lista dem alla, men det kommer att vara användbart att täcka några av de viktigaste som är tillämpliga på vår testfråga. Det kommer fortfarande att finnas för många för en enda lista, så jag delar upp dem i (något godtyckliga) avsnitt:

1. Initiering av föräldrakontext

När vi skickar in satsen för exekvering initieras den överordnade uppgiftens kontext (exekveringskontext noll) med:

  • En referens till bastransaktionen (explicit, implicit eller auto-commit). Parallella arbetare kommer att köra deltransaktioner, men de omfattas alla av bastransaktionen.
  • En lista med parametrar på satsen och deras nuvarande värden.
  • Ett primärt minnesobjekt (PMO) som används för att hantera minnesanslag och tilldelningar.
  • En länkad karta av operatörerna (frågenoder) i den körbara planen.
  • En fabrik för alla nödvändiga stora objekt (blob) handtag.
  • Lås klasser för att hålla reda på flera lås som hålls under en period under körning. Alla planer kräver inte låsklasser eftersom heltströmmande operatörer vanligtvis låser och låser upp enskilda rader i följd.
  • Det beräknade minnesbidraget för frågan.
  • Bevilja feedback i radlägesminne strukturer för varje operatör (SQL Server 2019).

Många av dessa saker kommer att användas eller hänvisas till av parallella uppgifter senare, så de måste finnas i överordnad omfattning först.

2. Metadata för överordnad kontext

Nästa huvuduppgifter som utförs är:

  • Kontrollerar den beräknade frågekostnaden är inom den gräns som anges av konfigurationsalternativen för kostnadsgräns för frågeregulatorn.
  • Uppdaterar indexanvändning poster – exponerade genom sys.dm_db_index_usage_stats .
  • Skapa cachade uttrycksvärden (körtidskonstanter).
  • Skapa en lista över profileringsoperatörer används för att samla in runtime-mått som radantal och timings, om detta har begärts för den aktuella körningen. Profilerna själva är inte skapade ännu, bara listan.
  • Ta en ögonblicksbild av väntningar för session waits-funktionen exponerad via sys.dm_exec_session_wait_stats .

3. DOP och minnesbidrag

Kontexten för överordnad uppgift nu:

  • Beräknar runtime grad av parallellism (DOP ). Detta påverkas av antalet lediga arbetare (se "DOP-nedgradering" tidigare), där de kan placeras bland noder och ett antal spårningsflaggor.
  • Reserverar erforderligt antal trådar. Det här steget är ren redovisning – själva trådarna kanske inte existerar vid denna tidpunkt. SQL Server håller reda på det maximala antalet trådar den är tillåten att ha. Reservera trådar subtraherar från det talet. När trådarna är klara ökas maxantalet igen.
  • Ställer in minnesbeviljande timeout .
  • Beräknar minnestillståndet, inklusive minne som krävs för utbytesbuffertar.
  • Erhåller minnesanslaget via lämplig resurssemafor .
  • Skapar ett förvaltarobjekt för att hantera parallella underprocesser . Förälderuppgiften är processen på högsta nivå; ytterligare uppgifter är också kända som underprocesser .

Även om trådar är "reserverade" vid det här laget, kan SQL Server fortfarande stöta på THREADPOOL väntar senare när den försöker använda en av de "reserverade" trådarna. Reservationen garanterar att SQL Server kommer att finnas kvar runt sitt konfigurerade maximala antal trådar hela tiden, men den fysiska tråden kanske inte är omedelbart tillgänglig från trådpoolen . När det händer kommer en ny tråd att behöva startas av operativsystemet, vilket kan ta en liten stund. För mer om det, se Unusual THREADPOOL Waits av Josh Darnell.

4. Inställning av sökfråga

Radlägesplaner körs i iterativ mode, med början vid roten. Planen vi har för tillfället är ännu inte kapabel att genomföra detta sätt. Det är fortfarande till stor del en mall, även om den redan innehåller en hel del exekveringsspecifik information.

SQL Server behöver konvertera den nuvarande strukturen till ett träd med iteratorer , var och en med metoder som Open , GetRow och Close . Iteratormetoderna är kopplade till sina barn via funktionspekare. Till exempel, anropa GetRow på roten anropar GetRow rekursivt på barnoperatörer tills en lövnivå nås och en rad börjar "bubbla upp" trädet. För en uppdatering av detaljerna se Iteratorer, frågeplaner och varför de körs baklänges.

Slutet av del 1

Vi har gjort goda framsteg med att sätta upp exekveringskontexten för den överordnade uppgiften. I del 2 kommer vi att följa med när SQL Server konstruerar frågesökningsträdet som krävs för iterativ exekvering.


  1. Hur flyttar jag min befintliga rails-app till heroku? (sqlite till postgres)

  2. Hur visar jag ett MySQL-fel i PHP för en lång fråga som beror på användarens input?

  3. SQL Server Texttyp kontra varchar datatyp

  4. Finns det något sätt att installera java på Oracle 11g XE?