Detta är den fjärde delen i en serie i fem delar som tar en djupdykning i hur parallella planer för SQL Server-radläge börjar köras. Del 1 initierade exekveringskontext noll för den överordnade uppgiften, och del 2 skapade frågesökningsträdet. Del 3 startade sökfrågan, utförde någon tidig fas bearbetning och startade de första ytterligare parallella uppgifterna i gren C.
Utförandedetaljer för filial C
Detta är det andra steget i exekveringssekvensen:
- Gren A (föräldrauppgift).
- Gren C (ytterligare parallella uppgifter).
- Gren D (ytterligare parallella uppgifter).
- Gren B (ytterligare parallella uppgifter).
En påminnelse om grenarna i vår parallellplan (klicka för att förstora)
En kort tid efter de nya uppgifterna för gren C är köade, SQL Server kopplar en arbetare till varje uppgift och placerar arbetaren på en schemaläggare redo för utförande. Varje ny uppgift körs i en ny körningskontext. På DOP 2 finns det två nya uppgifter, två arbetstrådar och två exekveringskontexter för gren C. Varje uppgift kör sin egen kopia av iteratorerna i gren C på sin egen arbetstråd:
De två nya parallella uppgifterna börjar köras vid en underprocedur ingångspunkt, som initialt leder till en Open
ring till producentsidan av börsen (CQScanXProducerNew::Open
). Båda uppgifterna har identiska samtalsstackar i början av livet:
Exchange-synkronisering
Under tiden föräldrauppgiften (kör på sin egen arbetstråd) registrerar de nya underprocesserna hos underprocesshanteraren och väntar på konsumentsidan av ompartitionsströmmarna utbyts vid nod 5. Den överordnade uppgiften väntar på CXPACKET
* tills alla av parallella uppgifter för gren C slutför deras Open
samtal och återgå till producentsidan av växeln. De parallella uppgifterna öppnar varje iterator i deras underträd (dvs. ner till indexsökningen vid nod 9 och tillbaka) innan de återgår till ompartitionsströmutbytet vid nod 5. Den överordnade uppgiften väntar på CXPACKET
medan detta händer. Kom ihåg att den överordnade uppgiften kör anrop i tidiga faser.
Vi kan se denna väntan i väntande uppgifter DMV:
Exekveringskontext noll (den överordnade uppgiften) blockeras av båda de nya exekveringskontexterna. Dessa exekveringskontexter är de första ytterligare som skapas efter kontext noll, så de tilldelas siffrorna ett och två. För att betona:Båda nya exekveringskontexterna måste öppna sina underträd och återgå till utbytet för den överordnade uppgiftens CXPACKET
vänta tills det tar slut.
Du kanske har förväntat dig att se CXCONSUMER
väntar här, men den väntan är reserverad för att vänta på raddata att anlända. Den nuvarande väntan är inte för rader — det är för producentsidan att öppna , så vi får en generisk CXPACKET
* vänta.
* Azure SQL Database och Managed Instance använder den nya CXSYNC_PORT
vänta istället för CXPACKET
här, men den förbättringen har inte kommit in i SQL Server än (från 2019 CU9).
Inspektera de nya parallella uppgifterna
Vi kan se de nya uppgifterna i frågeprofilerna DMV. Profileringsinformation för de nya uppgifterna visas i DMV eftersom deras exekveringskontext härleddes (klonades, sedan uppdaterades) från föräldern (exekveringskontext noll):
Det finns nu tre poster för varje iterator i gren C (markerad). En för den överordnade uppgiften (exekveringskontext noll), och en för varje ny ytterligare parallell uppgift (kontext 1 och 2). Observera att den uppskattade raden per tråd räknas (se del 1) har kommit nu, och visas endast för de parallella uppgifterna. De första och sista aktiva tiderna för de parallella uppgifterna representerar den tidpunkt då deras exekveringskontext skapades. Ingen av de nya uppgifterna har öppnat några iteratorer ännu.
Ompartitionsströmmarna Exchange vid nod 5 har fortfarande bara en enda ingång i DMV-utgången. Detta beror på att den associerade osynliga profileraren övervakar konsumenten sidan av utbytet. De ytterligare parallella uppgifterna ligger hos producenten sidan av utbytet. Konsumentsidan av nod 5 kommer så småningom har parallella uppgifter, men vi har inte kommit till den punkten än.
Checkpoint
Detta verkar vara en bra punkt för att stanna upp för att andas och sammanfatta var allt är för tillfället. Det kommer att bli fler av dessa stopppunkter allt eftersom.
- Föräldrauppgiften är på konsumentsidan av ompartitionsströmmarna utbyts vid nod 5 , väntar på
CXPACKET
. Det är mitt i exekveringen av anrop i tidiga faser. Den pausade för att starta gren C eftersom den grenen innehåller en blockerande sortering. Den överordnade uppgiftens väntan kommer att fortsätta tills båda parallella uppgifter har öppnat sina underträd. - Två nya parallella uppgifter på producentsidan av nod 5-växeln är redo att öppna iteratorerna i gren C.
Ingenting utanför gren C i denna parallella exekveringsplan kan göra framsteg förrän den överordnade uppgiften släpps från dess CXPACKET
vänta. Kom ihåg att vi bara har skapat en uppsättning ytterligare parallella arbetare hittills, för gren C. Den enda andra tråden är den överordnade uppgiften, och den är blockerad.
Branch C Parallell Execution
De två parallella uppgifterna börjar på producentsidan av ompartitionsströmmarna utbyts vid nod 5. Var och en har en separat (seriell) plan med sin egen strömaggregat, sortering och indexsökning. Beräkningsskalären visas inte i körtidsplanen eftersom dess beräkningar skjuts upp till sorteringen.
Varje instans av indexsökningen är parallellmedveten och fungerar på osammanhängande uppsättningar av rader. Dessa uppsättningar genereras på begäran från den överordnade raduppsättningen som skapats tidigare av den överordnade uppgiften (behandlas i del 1). När någon av instanserna av sökningen behöver ett nytt underområde av rader, synkroniseras det med de andra arbetartrådarna, så att endast en tilldelar ett nytt underområde samtidigt. Synkroniseringsobjektet som användes skapades också tidigare av den överordnade uppgiften. När en uppgift väntar på exklusiv åtkomst till den överordnade raduppsättningen för att få ett nytt underområde, väntar den på CXROWSET_SYNC
.
Banch C-uppgifter öppna
Sekvensen för Open
anrop för varje uppgift i gren C är:
CQScanXProducerNew::Open
. Observera att det inte finns någon föregående profilerare på producentsidan av en börs. Detta är olyckligt för frågemottagare.CXTransLocal::Open
CXPort::Register
CXTransLocal::ActivateWorkers
CQScanProfileNew::Open
. Profileraren ovanför nod 6.CQScanStreamAggregateNew::Open
(nod 6)CQScanProfileNew::Open
. Profileraren ovanför nod 7.CQScanSortNew::Open
(nod 7)
Sorten är en helt blockerande operatör . Den förbrukar hela sin input under dess Open
ringa upp. Det finns ett stort antal intressanta interna detaljer att utforska här, men utrymmet är kort, så jag kommer bara att täcka höjdpunkterna:
sorteringen bygger sin sorteringstabell genom att öppna dess underträd och konsumera alla rader som dess barn kan tillhandahålla. När sorteringen är klar är sorteringen redo att gå över till utdataläge och den återför kontrollen till sin överordnade. Sorteringen kommer senare att svara på GetRow()
samtal och returnerar nästa sorterade rad varje gång. En illustrativ anropsstack under sorteringsinmatning är:
Körningen fortsätter tills varje sortering har förbrukat alla (osammanhängande intervall av) rader som är tillgängliga från dess underordnade indexsökning . Sorteringarna anropar sedan Close
på indexsökningarna och återför kontrollen till deras överordnade strömningsaggregat . Strömaggregaten initierar sina räknare och återför kontrollen till producenten sidan av ompartitionsväxeln vid nod 5. Sekvensen Open
samtal är nu klara i den här grenen.
Profilerings-DMV vid denna tidpunkt visar uppdaterade tidsnummer och stängningstider för parallellindexet söker:
Mer börssynkronisering
Kom ihåg att föräldrauppgiften väntar på konsumenten sidan av nod 5 för alla producenter att öppna. En liknande synkroniseringsprocess sker nu bland de parallella uppgifterna på producenten sidan av samma börs:
Varje producentuppgift synkroniseras med de andra via CXTransLocal::Synchronize
. Tillverkarna kallar CXPort::Open
, vänta sedan på CXPACKET
för alla konsumenter parallella uppgifter att öppna. När den första parallella gren C-uppgiften kommer tillbaka till producentsidan av börsen och väntar, ser de väntande uppgifterna DMV ut så här:
Vi har fortfarande väntan på föräldrauppgiftens konsumentsida. Den nya CXPACKET
belyst är vår första parallella uppgift på producentsidan som väntar på alla parallella uppgifter på konsumentsidan för att öppna utbytesporten.
De parallella uppgifterna på konsumentsidan (i gren B) existerar inte ens ännu, så producentuppgiften visar NULL för exekveringskontexten den är blockerad av. Uppgiften som för närvarande väntar på konsumentsidan av ompartitionsströmutbytet är den överordnade uppgiften (inte en parallell uppgift!) som kör EarlyPhases
kod, så det räknas inte.
Föräldrauppgiften CXPACKET vänta slutar
När den andra parallell uppgift i gren C kommer tillbaka till producentsidan av börsen från dess Open
samtal, alla producenter har öppnat växelporten, så förälderuppgiften på konsumentsidan av utbytet är släppt från dess CXPACKET
vänta.
Arbetarna på producentsidan fortsätter att vänta på att parallella uppgifter på konsumentsidan ska skapas och öppnar utbytesporten:
Checkpoint
Vid denna tidpunkt:
- Det finns totalt tre uppgifter:Två i gren C, plus den överordnade uppgiften.
- Båda producenterna vid nod 5 har växeln öppnat och väntar på
CXPACKET
för konsumentsidan att parallella uppgifter öppnas. Mycket av utbytesmaskineriet (inklusive radbuffertar) skapas av konsumentsidan, så det finns ingenstans för producenterna att lägga rader ännu. - De sorterar i gren C har förbrukat all sin input och är redo att tillhandahålla sorterad utdata.
- Indexsökningar i gren C har avslutat sitt arbete och lagt ner.
- Föräldrauppgiften har precis släppts från att vänta på
CXPACKET
på konsumentsidan av noden 5 utbyter uppdelningsströmmar. Det är fortfarande kör kapsladeEarlyPhases
samtal.
Branch D Parallel Tasks Start
Detta är det tredje steget i exekveringssekvensen:
- Gren A (föräldrauppgift).
- Gren C (ytterligare parallella uppgifter).
- Gren D (ytterligare parallella uppgifter).
- Gren B (ytterligare parallella uppgifter).
Frisläppt från dess CXPACKET
vänta på konsumentsidan av ompartitionsströmutbytet vid nod 5, förälderuppgiften stiger upp frågesökningsträdet för gren B. Den returnerar från kapslade EarlyPhases
anrop till de olika iteratorerna och profilerna på den yttre (övre) ingången för sammanfogningen.
Som nämnt, stigande trädet uppdaterar förfluten tid och CPU-tider som registrerats av de osynliga profileringsiteratorerna. Vi kör kod med den överordnade uppgiften, så dessa siffror registreras mot exekveringskontext noll. Detta är den ultimata källan till "tråd 0"-tidssiffrorna som hänvisades till i min tidigare artikel, Understanding Execution Plan Operator Timings.
Väl tillbaka vid sammanfogningen anropar den överordnade uppgiften EarlyPhases
för iteratorerna och profilerna på den inre (nedre) ingången till sammanfogningen. Dessa är noderna 10 till 15 (exklusive 14, som är uppskjuten):
När den överordnade uppgiftens tidiga fasanrop når indexsökningen vid nod 15, börjar den stiga upp i trädet igen (ställer in profileringstider) tills den når ompartitionsströmutbytet vid nod 11.
Sedan, precis som den gjorde på den yttre (övre) ingången till sammanfogningen, startar den producentsidan av växeln vid nod 11 , skapa två nya parallella uppgifter .
Detta sätter gren D igång (visas nedan). Gren D körs exakt som redan beskrivits i detalj för gren C.
Omedelbart efter start av uppgifter för gren D väntar förälderuppgiften på CXPACKET
vid nod 11 för de nya producenterna att öppna utbytesporten:
Den nya CXPACKET
väntar är markerade. Observera att det rapporterade nod-id:t kan vara lite missvisande. Den överordnade uppgiften väntar verkligen på konsumentsidan av nod 11 (omfördelningsströmmar), inte nod 2 (samla strömmar). Detta är en egenhet med tidig fasbehandling.
Under tiden fortsätter producenttrådarna i Branch C att vänta på CXPACKET
för konsumentsidan av nod 5 byts ompartitionsströmmar ut.
Gren D-öppning
Precis efter att den överordnade uppgiften startar producenterna för gren D, frågeprofilen DMV visar de nya exekveringskontexterna (3 och 4):
De två nya parallella uppgifterna i gren D fortsätt precis som de i gren C gjorde. Sorteringarna förbrukar all sin input, och Branch D-uppgifterna återgår till utbytet. Detta frigör den överordnade uppgiften från dess CXPACKET
vänta. Branch D-arbetarna väntar sedan på CXPACKET
på producentsidan av nod 11 för att parallella uppgifter för konsumentsidan ska öppnas. Dessa parallellarbetare (i gren B) finns inte ännu.
Checkpoint
De väntande uppgifterna vid denna tidpunkt visas nedan:
Båda uppsättningarna av parallella uppgifter i grenarna C och D väntar på CXPACKET
för att deras parallella uppgiftskonsumenter ska öppna, byter strömmar noder 5 och 11 vid ompartitionsströmmar. Den enda körbara uppgiften i hela frågan just nu är förälderuppgiften .
Frågeprofileraren DMV vid denna tidpunkt visas nedan, med operatörer i grenarna C och D markerade:
De enda parallella uppgifterna vi inte har påbörjat ännu är i gren B. Allt arbete i gren B hittills har varit tidiga faser samtal som utförs av föräldrauppgiften .
Slutet av del 4
I den sista delen av denna serie kommer jag att beskriva hur resten av denna speciella parallella genomförandeplan startar, och kortfattat täcka hur planen ger resultat. Jag avslutar med en mer allmän beskrivning som gäller parallella planer av godtycklig komplexitet.