Dit is het vierde deel in een vijfdelige serie die een diepe duik neemt in de manier waarop parallelle plannen in de rijmodus van SQL Server worden uitgevoerd. In deel 1 werd uitvoeringscontext nul geïnitialiseerd voor de bovenliggende taak en in deel 2 werd de query-scanstructuur gemaakt. Deel 3 startte de query-scan, voerde een vroege fase uit verwerking, en startte de eerste extra parallelle taken in tak C.
Uitvoeringsdetails van Branch C
Dit is de tweede stap van de uitvoeringsreeks:
- Tak A (bovenliggende taak).
- Branch C (extra parallelle taken).
- Branch D (extra parallelle taken).
- Branch B (extra parallelle taken).
Een herinnering aan de filialen in ons parallelplan (klik om te vergroten)
Korte tijd na de nieuwe taken voor branch C in de wachtrij staan, SQL Server koppelt een werknemer aan elke taak, en plaatst de werknemer op een planner klaar voor uitvoering. Elke nieuwe taak wordt uitgevoerd binnen een nieuwe uitvoeringscontext. Bij DOP 2 zijn er twee nieuwe taken, twee werkthreads en twee uitvoeringscontexten voor tak C. Elke taak voert zijn eigen kopie van de iterators in tak C uit op zijn eigen werkthread:
De twee nieuwe parallelle taken beginnen met een subprocedure ingangspunt, dat in eerste instantie leidt tot een Open
aanroepen aan de producentenkant van de centrale (CQScanXProducerNew::Open
). Beide taken hebben aan het begin van hun leven identieke call-stacks:
Exchange-synchronisatie
Ondertussen is de oudertaak (draaiend op zijn eigen werkthread) registreert de nieuwe subprocessen bij de subprocesmanager en wacht aan de consumentenkant van de herverdelingsstromen wisselen op knooppunt 5. De bovenliggende taak wacht op CXPACKET
* tot alle van de parallelle taken van branch C voltooien hun Open
oproepen en keert terug naar de producentenkant van de centrale. De parallelle taken openen elke iterator in hun substructuur (d.w.z. tot aan de index zoeken bij knooppunt 9 en terug) voordat u terugkeert naar de uitwisseling van herverdelingsstromen bij knooppunt 5. De bovenliggende taak wacht op CXPACKET
terwijl dit gebeurt. Onthoud dat de bovenliggende taak het uitvoeren van oproepen in de vroege fase is.
We kunnen dit wachten zien in de wachttaken DMV:
Uitvoeringscontext nul (de bovenliggende taak) wordt geblokkeerd door beide nieuwe uitvoeringscontexten. Deze uitvoeringscontexten zijn de eerste extra die worden gemaakt na context nul, dus krijgen ze de nummers één en twee toegewezen. Om te benadrukken:beide nieuwe uitvoeringscontexten moeten hun subbomen openen en terugkeren naar de uitwisseling voor de CXPACKET
van de bovenliggende taak wacht tot het is afgelopen.
Je had misschien verwacht CXCONSUMER
. te zien wacht hier, maar dat wachten is gereserveerd voor wachten op rijgegevens aankomen. De huidige wachttijd is niet voor rijen — het is aan de kant van de producent om te openen , dus we krijgen een generieke CXPACKET
* wacht.
* Azure SQL Database en Managed Instance gebruiken de nieuwe CXSYNC_PORT
wacht in plaats van CXPACKET
hier, maar die verbetering is nog niet doorgevoerd in SQL Server (vanaf 2019 CU9).
De nieuwe parallelle taken inspecteren
We kunnen de nieuwe taken zien in de zoekprofielen DMV. Profileringsinformatie voor de nieuwe taken verschijnt in de DMV omdat hun uitvoeringscontexten zijn afgeleid (gekloond en vervolgens bijgewerkt) van de bovenliggende (uitvoeringscontext nul):
Er zijn nu drie items voor elke iterator in Branch C (gemarkeerd). Eén voor de bovenliggende taak (uitvoeringscontext nul) en één voor elke nieuwe aanvullende parallelle taak (contexten 1 en 2). Merk op dat het geschatte aantal rijen per thread (zie deel 1) zijn nu aangekomen en worden alleen getoond voor de parallelle taken. De eerste en laatste actieve tijden want de parallelle taken vertegenwoordigen het tijdstip waarop hun uitvoeringscontexten werden gecreëerd. Geen van de nieuwe taken is geopend nog geen iterators.
De herverdelingsstromen uitwisseling op knooppunt 5 heeft nog steeds slechts één invoer in de DMV-uitvoer. Dit komt omdat de bijbehorende onzichtbare profiler de consument in de gaten houdt kant van de beurs. De extra parallelle taken liggen bij de producent kant van de beurs. De consumentenkant van knooppunt 5 zal uiteindelijk parallelle taken hebben, maar zover zijn we nog niet.
Checkpoint
Dit lijkt een goed punt om even op adem te komen en samen te vatten waar alles op dit moment staat. Er zullen meer van deze stopplaatsen zijn naarmate we verder gaan.
- De oudertaak is aan de consumentenkant van de herverdelingsstromen worden uitgewisseld op knooppunt 5 , wachtend op
CXPACKET
. Het bevindt zich midden in het uitvoeren van oproepen in de vroege fasen. Het pauzeerde om Branch C op te starten omdat die branch een blokkerende sortering bevat. Het wachten van de bovenliggende taak gaat door totdat beide parallelle taken het openen van hun subbomen hebben voltooid. - Twee nieuwe parallelle taken aan de kant van de producent van de node 5 exchange zijn klaar om de iterators in Branch C te openen.
Niets buiten Tak C van dit parallelle uitvoeringsplan kan vooruitgang boeken totdat de bovenliggende taak wordt vrijgegeven uit zijn CXPACKET
wacht. Onthoud dat we tot nu toe slechts één set extra parallelle werkers hebben gemaakt, voor Branch C. De enige andere thread is de bovenliggende taak, en die is geblokkeerd.
Branch C parallelle uitvoering
De twee parallelle taken beginnen bij de producentenkant van de herverdelingsstromen wisselen op knooppunt 5. Elk heeft een afzonderlijk (serieel) plan met zijn eigen stroomaggregatie-, sorteer- en indexzoekfunctie. De compute-scalar verschijnt niet in het runtime-plan omdat de berekeningen worden uitgesteld tot de sortering.
Elke instantie van de indexzoekopdracht is parallelbewust en werkt op onsamenhangende reeksen rijen. Deze sets worden op aanvraag gegenereerd op basis van de bovenliggende rijenset die eerder door de bovenliggende taak is gemaakt (behandeld in deel 1). Wanneer een van beide instanties van de seek een nieuw subbereik van rijen nodig heeft, wordt het gesynchroniseerd met de andere werkthreads, zodat er slechts één tegelijkertijd een nieuw subbereik toewijst. Het gebruikte synchronisatieobject is ook eerder gemaakt door de bovenliggende taak. Wanneer een taak wacht op exclusieve toegang tot de bovenliggende rijenset om een nieuw subbereik te verkrijgen, wacht deze op CXROWSET_SYNC
.
Tak C-taken geopend
De volgorde van Open
oproepen voor elke taak in Branch C is:
CQScanXProducerNew::Open
. Merk op dat er geen voorafgaande profiler is aan de producentenkant van een uitwisseling. Dit is jammer voor query-tuners.CXTransLocal::Open
CXPort::Register
CXTransLocal::ActivateWorkers
CQScanProfileNew::Open
. De profiler boven knooppunt 6.CQScanStreamAggregateNew::Open
(knooppunt 6)CQScanProfileNew::Open
. De profiler boven knooppunt 7.CQScanSortNew::Open
(knooppunt 7)
De sortering is een volledig blokkerende operator . Het verbruikt zijn volledige invoer tijdens zijn Open
telefoongesprek. Er zijn een groot aantal interessante interne details om hier te verkennen, maar de ruimte is kort, dus ik zal alleen de hoogtepunten behandelen:
De sorteer bouwt zijn sorteertabel door zijn subboom te openen en alle rijen te consumeren die zijn kinderen kunnen bieden. Zodra het sorteren is voltooid, is het sorteren klaar om over te gaan naar de uitvoermodus en wordt de controle teruggegeven aan het bovenliggende element. De sortering zal later reageren op GetRow()
oproepen, waarbij telkens de volgende gesorteerde rij wordt geretourneerd. Een illustratieve call-stack tijdens sorteerinvoer is:
De uitvoering gaat door totdat elke sortering alle (disjuncte reeksen van) beschikbare rijen van zijn onderliggende indexzoekopdracht heeft verbruikt . De sorteringen roepen dan Close
. aan op de index zoekt en geeft controle terug aan hun bovenliggende stroomaggregaat . De stream-aggregaten initialiseren hun tellers en geven de controle terug aan de producent kant van de repartitie-uitwisseling op knooppunt 5. De volgorde van Open
oproepen is nu voltooid in deze tak.
De profilerings-DMV toont op dit moment bijgewerkte timingnummers en sluitingstijden voor de parallelle index zoekt:
Meer uitwisselingssynchronisatie
Bedenk dat de bovenliggende taak wacht op de consument kant van knooppunt 5 voor alle producenten om te openen. Een soortgelijk synchronisatieproces vindt nu plaats tussen de parallelle taken op de producent kant van dezelfde centrale:
Elke producertaak wordt gesynchroniseerd met de andere via CXTransLocal::Synchronize
. De producenten noemen CXPort::Open
, wacht dan op CXPACKET
voor alle consumentenkant parallelle taken te openen. Wanneer de eerste parallelle taak van Branch C terugkomt aan de producentenkant van de centrale en wacht, zien de wachtende taken DMV er als volgt uit:
We hebben nog steeds de wacht aan de kant van de consument van de bovenliggende taak. De nieuwe CXPACKET
gemarkeerd is onze eerste parallelle taak aan de producent, wachtend op alle parallelle taken aan de consumentenzijde om de uitwisselingspoort te openen.
De parallelle taken aan de consumentenzijde (in Branch B) bestaan nog niet eens, dus de producenttaak geeft NULL weer voor de uitvoeringscontext waardoor deze wordt geblokkeerd. De taak die momenteel wacht aan de consumentenkant van de uitwisseling van herpartitioneringsstromen is de bovenliggende taak (geen parallelle taak!) die EarlyPhases
uitvoert code, dus het telt niet.
Oudertaak CXPACKET wacht eindigt
Wanneer de tweede parallelle taak in filiaal C komt terug aan de producentkant van de centrale vanuit zijn Open
oproepen, hebben alle producenten de uitwisselingspoort geopend, dus de oudertaak aan de consumentenkant van de uitwisseling is vrijgegeven van zijn CXPACKET
wacht.
De arbeiders aan de producentenkant blijven wachten tot er parallelle taken aan de consumentenkant zijn gemaakt en openen de uitwisselingspoort:
Checkpoint
Op dit moment:
- Er zijn in totaal drie taken:twee in tak C, plus de bovenliggende taak.
- Beide producenten bij het knooppunt 5 zijn de uitwisselingen geopend en wachten op
CXPACKET
voor de kant van de consument parallelle taken te openen. Veel van de uitwisselingsmachines (inclusief rijbuffers) worden gecreëerd door de consument, dus de producenten kunnen nog nergens rijen plaatsen. - De soorten in Branch C hebben al hun input verbruikt en zijn klaar om gesorteerde output te leveren.
- De index zoekt in Branch C hebben hun werk voltooid en zijn gesloten.
- De oudertaak is zojuist ontheven van het wachten op
CXPACKET
aan de consumentenzijde van het knooppunt 5 uitwisseling van herverdelingsstromen. Het is nog uitvoeren van genesteEarlyPhases
oproepen.
Branch D parallelle taken starten
Dit is de derde stap in de uitvoeringsvolgorde:
- Tak A (bovenliggende taak).
- Branch C (extra parallelle taken).
- Branch D (extra parallelle taken).
- Branch B (extra parallelle taken).
Vrijgegeven van zijn CXPACKET
wacht aan de consumentenkant van de uitwisseling van herverdelingsstromen op knooppunt 5, de bovenliggende taak stijgt de Branch B-queryscanboom. Het komt terug uit geneste EarlyPhases
oproepen naar de verschillende iterators en profilers op de buitenste (bovenste) ingang van de merge join.
Zoals gezegd, oplopend de boom werkt de verstreken en CPU-tijden bij die zijn geregistreerd door de onzichtbare profileringiterators. We voeren code uit met behulp van de bovenliggende taak, dus die getallen worden vastgelegd tegen uitvoeringscontext nul. Dit is de ultieme bron van de "thread 0"-timingnummers waarnaar wordt verwezen in mijn vorige artikel, Inzicht in de timing van de operators van het uitvoeringsplan.
Eenmaal terug bij de merge join roept de bovenliggende taak EarlyPhases
. aan voor de iterators en profilers op de binnenste (lagere) invoer voor de samenvoegverbinding. Dit zijn knooppunten 10 tot 15 (exclusief 14, die wordt uitgesteld):
Zodra de oproepen van de vroege fasen van de bovenliggende taak de index-zoekopdracht bij knooppunt 15 bereiken, begint deze opnieuw in de boom te stijgen (profileringstijden instellen) totdat het de uitwisseling van herverdelingsstromen bij knooppunt 11 bereikt.
Dan, net als bij de buitenste (bovenste) invoer van de merge join, start het de producer-kant van de uitwisseling op knooppunt 11 , het creëren van twee nieuwe parallelle taken .
Dit zet Branch D in beweging (zie hieronder). Branch D voert precies uit zoals al in detail beschreven voor Branch C.
Onmiddellijk na het starten van taken voor Branch D, wacht de oudertaak op CXPACKET
bij knooppunt 11 voor de nieuwe producenten om de uitwisselingspoort te openen:
De nieuwe CXPACKET
wachten zijn gemarkeerd. Merk op dat de gerapporteerde node-ID een beetje misleidend kan zijn. De bovenliggende taak wacht echt aan de consumentenkant van knooppunt 11 (herpartitioneerstromen), niet knooppunt 2 (verzamelen van stromen). Dit is een eigenaardigheid van verwerking in de vroege fase.
Ondertussen blijven de producententhreads in Branch C wachten op CXPACKET
voor de consumentenkant van het knooppunt 5 repartitie streams uitwisseling om te openen.
Tak D-opening
Net nadat de bovenliggende taak de producenten voor Branch D start, het zoekprofiel DMV toont de nieuwe uitvoeringscontexten (3 en 4):
De twee nieuwe parallelle taken in tak D precies zo te werk gaan als in tak C. De soorten verbruiken al hun input en de Tak D-taken keren terug naar de centrale. Dit geeft de bovenliggende taak vrij van zijn CXPACKET
wacht. De medewerkers van Branch D wachten vervolgens op CXPACKET
aan de producentenkant van knooppunt 11 om parallelle taken aan de consumentenkant te openen. Die parallelle werkers (in Branch B) bestaan nog niet.
Checkpoint
De wachttaken op dit punt worden hieronder weergegeven:
Beide sets parallelle taken in Branch C en D wachten op CXPACKET
voor hun parallelle taak wisselen consumenten bij herverdelingsstromen knooppunten 5 en 11 uit. De enige uitvoerbare taak in de hele query is nu de bovenliggende taak .
De queryprofiler DMV op dit punt wordt hieronder weergegeven, met operators in takken C en D gemarkeerd:
De enige parallelle taken waar we nog niet mee begonnen zijn, zijn in Branch B. Al het werk in Branch B tot nu toe is vroege fasen oproepen uitgevoerd door de bovenliggende taak .
Einde van deel 4
In het laatste deel van deze serie zal ik beschrijven hoe de rest van dit specifieke parallelle uitvoeringsplan opstart, en kort bespreken hoe het plan resultaten oplevert. Ik zal eindigen met een meer algemene beschrijving die van toepassing is op parallelle plannen van willekeurige complexiteit.