sql >> Databasteknik >  >> RDS >> Database

Optimeringströsklar – gruppering och aggregering av data, del 4

Den här artikeln är den fjärde i en serie om optimeringströsklar. Serien omfattar gruppering och aggregering av data, förklarar de olika algoritmerna som SQL Server kan använda, och kostnadsmodellen som hjälper den att välja mellan algoritmerna. I den här artikeln fokuserar jag på parallellitetsöverväganden. Jag täcker de olika parallellitetsstrategier som SQL Server kan använda, tröskelvärdena för att välja mellan en seriell och en parallell plan, och kostnadslogiken som SQL Server tillämpar med ett koncept som kallas grad av parallellitet för kostnadsberäkning (DOP för kostnadsberäkning).

Jag kommer att fortsätta använda tabellen dbo.Orders i PerformanceV3-exempeldatabasen i mina exempel. Innan du kör exemplen i den här artikeln, kör följande kod för att släppa ett par onödiga index:

SLÄPP INDEX OM FINNS idx_nc_sid_od_cid PÅ dbo.Orders;SLÄPP INDEX OM FINNS idx_unc_od_oid_i_cid_eid PÅ dbo.Orders;

De enda två indexen som ska finnas kvar i den här tabellen är idx_cl_od (klustrade med orderdate som nyckel) och PK_Orders (icke-klustrade med orderid som nyckel).

Parallellismstrategier

Förutom att behöva välja mellan olika grupperings- och aggregeringsstrategier (förbeställt Stream Aggregate, Sort + Stream Aggregate, Hash Aggregate), behöver SQL Server också välja om den ska gå med en seriell eller en parallell plan. Faktum är att den kan välja mellan flera olika parallellitetsstrategier. SQL Server använder kostnadslogik som resulterar i optimeringströsklar som under olika förutsättningar gör att en strategi föredras framför de andra. Vi har redan diskuterat ingående kostnadslogiken som SQL Server använder i serieplaner i de tidigare delarna av serien. I det här avsnittet kommer jag att introducera ett antal parallellitetsstrategier som SQL Server kan använda för att hantera gruppering och aggregering. Inledningsvis kommer jag inte att gå in på detaljerna i kostnadslogiken, utan bara beskriva de tillgängliga alternativen. Senare i artikeln kommer jag att förklara hur kalkylformlerna fungerar och en viktig faktor i dessa formler som kallas DOP för kalkylering.

Som du kommer att lära dig senare tar SQL Server hänsyn till antalet logiska CPU:er i maskinen i sina kostnadsformler för parallella planer. I mina exempel, om jag inte säger annat, antar jag att målsystemet har 8 logiska processorer. Om du vill prova exemplen som jag kommer att ge, för att få samma planer och kostnadsvärden som jag, måste du köra koden på en maskin med 8 logiska CPU:er också. Om din maskin råkar ha ett annat antal processorer kan du emulera en maskin med 8 processorer – för kostnadsskäl – som så:

DBCC OPTIMIZER_WHATIF(CPU:er, 8);

Även om det här verktyget inte är officiellt dokumenterat och stöds, är det ganska praktiskt för forskning och lärande.

Tabellen Order i vår exempeldatabas har 1 000 000 rader med order-ID:n i intervallet 1 till 1 000 000. För att visa tre olika parallellitetsstrategier för gruppering och aggregering, filtrerar jag order där order-ID är större än eller lika med 300 001 (700 000 matchningar) och grupperar data på tre olika sätt (efter custid [20 000 grupper före filtrering], efter empid [500 grupper] och efter shipperid [5 grupper]), och beräkna antalet beställningar per grupp.

Använd följande kod för att skapa index för att stödja de grupperade frågorna:

CREATE INDEX idx_oid_i_eid ON dbo.Orders(orderid) INCLUDE(empid);CREATE INDEX idx_oid_i_sid ON dbo.Orders(orderid) INCLUDE(shipperid);CREATE INDEX idx_oid_i_cid ON dbo.Orders); före> 

Följande frågor implementerar ovannämnda filtrering och gruppering:

-- Fråga 1:Serial SELECT custid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY custidOPTION(MAXDOP 1); -- Fråga 2:Parallell, inte lokal/global SELECT custid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY custid; -- Fråga 3:Lokal parallell global parallell SELECT empid, COUNT(*) AS numordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY empid; -- Fråga 4:Lokal parallell global seriell SELECT shipperid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY shipperid;

Lägg märke till att fråga 1 och fråga 2 är samma (båda grupperas efter custid), bara den förra tvingar fram en serieplan och den senare får en parallell plan på en maskin med 8 processorer. Jag använder dessa två exempel för att jämföra seriella och parallella strategier för samma fråga.

Figur 1 visar de uppskattade planerna för alla fyra frågorna:

Figur 1:Parallellismstrategier

För nu, oroa dig inte för kostnadsvärdena som visas i figuren och omnämnandet av termen DOP för kostnadsberäkning. Jag kommer till dem senare. Fokusera först på att förstå strategierna och skillnaderna mellan dem.

Strategin som används i serieplanen för fråga 1 bör vara bekant för dig från de tidigare delarna i serien. Planen filtrerar de relevanta beställningarna med en sökning i det täckande indexet du skapade tidigare. Sedan, med det uppskattade antalet rader som ska grupperas och aggregeras, föredrar optimeraren strategin Hash Aggregate framför strategin Sortera + Stream Aggregate.

Planen för fråga 2 använder en enkel parallellitetsstrategi som endast använder en aggregerad operatör. En parallell Index Seek-operator distribuerar paket med rader till de olika trådarna på ett round-robin-sätt. Varje paket med rader kan innehålla flera distinkta kund-ID:n. För att en enda aggregatoperator ska kunna beräkna det korrekta slutliga gruppantal måste alla rader som tillhör samma grupp hanteras av samma tråd. Av denna anledning används en parallellism (Repartition Streams) utbytesoperatör för att dela upp strömmarna efter grupperingsuppsättningen (custid). Slutligen används en parallellism (Gather Streams) utbytesoperator för att samla strömmarna från de flera trådarna till en enda ström av resultatrader.

Planerna för Query 3 och Query 4 använder en mer komplex parallellitetsstrategi. Planerna börjar på samma sätt som planen för Query 2 där en parallell Index Seek-operator distribuerar paket med rader till olika trådar. Därefter utförs aggregeringsarbetet i två steg:en aggregeringsoperator grupperar och aggregerar lokalt raderna i den aktuella tråden (märk på partialagg1004-resultatmedlemmen), och en andra aggregeringsoperator grupperar och aggregerar resultaten från de lokala aggregaten globalt (märk globalagg1005) resultatmedlem). Vart och ett av de två aggregerade stegen – lokalt och globalt – kan använda vilken som helst av de aggregerade algoritmerna som jag beskrev tidigare i serien. Båda planerna för Query 3 och Query 4 börjar med ett lokalt Hash Aggregate och fortsätter med ett globalt Sort + Stream Aggregate. Skillnaden mellan de två är att den förra använder parallellitet i båda stegen (därav används ett Repartition Streams-utbyte mellan de två och ett Gather Streams-utbyte efter det globala aggregatet), och det senare hanterar det lokala aggregatet i en parallell zon och den globala samlas i en seriell zon (därav används ett Gather Streams-utbyte mellan de två).

När du gör din forskning om frågeoptimering i allmänhet, och parallellism specifikt, är det bra att vara bekant med verktyg som gör att du kan kontrollera olika optimeringsaspekter för att se deras effekter. Du vet redan hur man tvingar fram en seriell plan (med en MAXDOP 1-tips) och hur man emulerar en miljö som, för kostnadsskäl, har ett visst antal logiska processorer (DBCC OPTIMIZER_WHATIF, med alternativet processorer). Ett annat praktiskt verktyg är frågetipset ENABLE_PARALLEL_PLAN_PREFERENCE (introducerat i SQL Server 2016 SP1 CU2), som maximerar parallelliteten. Vad jag menar med detta är att om en parallell plan stöds för frågan så kommer parallellitet att föredras i alla delar av planen som kan hanteras parallellt, som om det vore gratis. Observera till exempel i figur 1 att planen för fråga 4 som standard hanterar det lokala aggregatet i en seriell zon och det globala aggregatet i en parallell zon. Här är samma fråga, bara den här gången med ENABLE_PARALLEL_PLAN_PREFERENCE frågetips tillämpad (vi kallar det fråga 5):

VÄLJ shipperid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY shipperidOPTION(ANVÄND HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'));

Planen för fråga 5 visas i figur 2:

Figur 2:Maximera parallellism

Observera att den här gången hanteras både de lokala och de globala aggregaten i parallella zoner.

Val av seriell/parallell plan

Kom ihåg att under frågeoptimering skapar SQL Server flera kandidatplaner och väljer den med den lägsta kostnaden bland de som den producerade. Termen kostnad är lite av en felaktig benämning eftersom kandidatplanen med den lägsta kostnaden enligt beräkningarna ska vara den med lägst körtid, inte den som har den lägsta mängden resurser som används totalt sett. Till exempel, mellan en seriekandidatplan och en parallell som produceras för samma fråga, kommer parallellplanen sannolikt att använda mer resurser, eftersom den behöver använda utbytesoperatorer som synkroniserar trådarna (distribuera, ompartitionera och samla strömmar). Men för att parallellplanen ska behöva mindre tid för att slutföra körningen än serieplanen måste besparingarna som uppnås genom att utföra arbetet med flera trådar uppväga det extra arbete som utförs av växeloperatörerna. Och detta måste återspeglas av kostnadsformlerna som SQL Server använder när parallellism är inblandad. Inte en enkel uppgift att göra exakt!

Förutom att kostnaden för den parallella planen måste vara lägre än kostnaden för serieplanen för att vara att föredra, måste kostnaden för serieplansalternativet vara större än eller lika med kostnadströskeln för parallellism . Detta är ett serverkonfigurationsalternativ inställt på 5 som standard som förhindrar att frågor med en ganska låg kostnad kan hanteras med parallellitet. Tanken här är att ett system med ett stort antal små frågor totalt sett skulle tjäna mer på att använda serieplaner, istället för att slösa mycket resurser på att synkronisera trådar. Du kan fortfarande ha flera frågor med serieplaner som körs samtidigt, och effektivt utnyttja maskinens multi-CPU-resurser. Faktum är att många SQL Server-proffs gillar att öka kostnadströskeln för parallellitet från standardvärdet 5 till ett högre värde. Ett system som kör ett ganska litet antal stora frågor samtidigt skulle dra mycket mer nytta av att använda parallella planer.

För att sammanfatta, för att SQL Server ska föredra en parallell plan framför det seriella alternativet, måste serieplanskostnaden vara minst kostnadströskeln för parallellitet, och parallellplanskostnaden måste vara lägre än serieplanskostnaden (vilket innebär potentiellt lägre körtid).

Innan jag kommer till detaljerna i de faktiska kostnadsformlerna, ska jag med exempel illustrera olika scenarier där ett val görs mellan seriella och parallella planer. Se till att ditt system antar 8 logiska processorer för att få frågekostnader som liknar mina om du vill prova exemplen.

Tänk på följande frågor (vi kallar dem Query 6 och Query 7):

-- Fråga 6:Serial SELECT empid, COUNT(*) AS numordersFROM dbo.OrdersWHERE orderid>=400001GROUP BY empid; -- Fråga 7:Påtvingad parallell SELECT empid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=400001GROUP BY empidOPTION(ANVÄND HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'));

Planerna för dessa frågor visas i figur 3.

Figur 3:Seriekostnad

Här är kostnaden för [tvingad] parallell plan lägre än kostnaden för serieplanen; dock är kostnaden för serieplanen lägre än standardkostnadströskeln för parallellitet på 5, därför valde SQL Server serieplanen som standard.

Tänk på följande frågor (vi kallar dem Fråga 8 och Fråga 9):

-- Fråga 8:Parallell SELECT empid, COUNT(*) AS numordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY empid; -- Fråga 9:Forcerad seriell SELECT empid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY empidOPTION(MAXDOP 1);

Planerna för dessa frågor visas i figur 4.

Figur 4:Seriekostnad>=kostnadströskel för parallellitet, parallell kostnad

Här är kostnaden för den [tvingade] serieplanen större än eller lika med kostnadströskeln för parallellitet, och kostnaden för parallellplanen är lägre än kostnaden för serieplanen, därför valde SQL Server parallellplanen som standard.

Tänk på följande frågor (vi kallar dem Fråga 10 och Fråga 11):

-- Fråga 10:Serial SELECT *FROM dbo.OrdersWHERE orderid>=100000; -- Fråga 11:Tvingad parallell SELECT *FROM dbo.OrdersWHERE orderid>=100000OPTION(ANVÄND HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'));

Planerna för dessa frågor visas i figur 5.

Figur 5:Seriekostnad>=kostnadströskel för parallellitet, parallell kostnad>=seriekostnad

Här är kostnaden för serieplanen större än eller lika med kostnadströskeln för parallellitet; dock är kostnaden för serieplanen lägre än den [tvingade] parallella plankostnaden, därför valde SQL Server serieplanen som standard.

Det finns en annan sak som du behöver veta om att försöka maximera parallelliteten med ENABLE_PARALLEL_PLAN_PREFERENCE-tipset. För att SQL Server ens ska kunna använda en parallell plan måste det finnas någon parallellitetsaktiverare som ett restpredikat, en sortering, ett aggregat och så vidare. En plan som bara tillämpar en indexskanning eller indexsökning utan ett kvarvarande predikat, och utan någon annan parallellitetsaktiverare, kommer att behandlas med en serieplan. Betrakta följande frågor som exempel (vi kallar dem Fråga 12 och Fråga 13):

-- Fråga 12 SELECT *FROM dbo.OrdersOPTION(USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE')); -- Fråga 13 SELECT *FROM dbo.OrdersWHERE orderid>=100000OPTION(ANVÄND TIP('ENABLE_PARALLEL_PLAN_PREFERENCE'));

Planerna för dessa frågor visas i figur 6.

Figur 6:Parallellismaktiverare

Fråga 12 får en serieplan trots ledtråden eftersom det inte finns någon möjlighet för parallellism. Fråga 13 får en parallell plan eftersom det finns ett kvarvarande predikat inblandat.

Beräknar och testar DOP för kostnadsberäkning

Microsoft var tvungen att kalibrera kostnadsformlerna i ett försök att ha en lägre parallellplanskostnad än serieplanskostnaden återspeglar en lägre körtid och vice versa. En potentiell idé var att ta serieoperatörens CPU-kostnad och helt enkelt dividera den med antalet logiska CPU:er i maskinen för att producera parallelloperatörens CPU-kostnad. Det logiska antalet processorer i maskinen är huvudfaktorn som bestämmer frågans grad av parallellitet, eller kort sagt DOP (antalet trådar som kan användas i en parallell zon i planen). Det förenklade tänkandet här är att om en operatör tar T-tidsenheter att slutföra när man använder en tråd, och frågans grad av parallellitet är D, skulle det ta operatörens T/D-tid att slutföra när man använder D-trådar. I praktiken är det inte så enkelt. Till exempel har du vanligtvis flera frågor som körs samtidigt och inte bara en, i vilket fall en enda fråga inte kommer att få alla maskinens CPU-resurser. Så Microsoft kom på idén om graden av parallellitet för kostnadsberäkning (DOP för kostnadsberäkning, kort sagt). Detta mått är vanligtvis lägre än antalet logiska CPU:er i maskinen och är den faktor som serieoperatörens CPU-kostnad divideras med för att beräkna parallelloperatörens CPU-kostnad.

Normalt beräknas DOP för kostnadsberäkning som antalet logiska processorer dividerat med 2, med hjälp av heltalsdivision. Det finns dock undantag. När antalet processorer är 2 eller 3, ställs DOP för kostnadsberäkning till 2. Med 4 eller fler processorer ställs DOP för kostnadsberäkning till #CPUs / 2, återigen, med heltalsdivision. Det är upp till ett visst maximum, vilket beror på mängden tillgängligt minne för maskinen. I en maskin med upp till 4 096 MB minne är den maximala DOP för kostnad 8; med mer än 4 096 MB, maximal DOP för kostnadsberäkning är 32.

För att testa denna logik vet du redan hur man emulerar ett önskat antal logiska processorer med DBCC OPTIMIZER_WHATIF, med alternativet processorer, så här:

DBCC OPTIMIZER_WHATIF(CPU:er, 8);

Genom att använda samma kommando med alternativet MemoryMBs kan du emulera en önskad mängd minne i MB, så här:

DBCC OPTIMIZER_WHATIF(MemoryMBs, 16384);

Använd följande kod för att kontrollera befintlig status för de emulerade alternativen:

DBCC TRACEON(3604); DBCC OPTIMIZER_WHATIF(Status); DBCC TRACEOFF(3604);

Använd följande kod för att återställa alla alternativ:

DBCC OPTIMIZER_WHATIF(ResetAll);

Här är en T-SQL-fråga som du kan använda för att beräkna DOP för kostnad baserat på ett inmatat antal logiska CPU:er och mängd minne:

DECLARE @NumCPUs AS INT =8, @MemoryMBs AS INT =16384; VÄLJ CASE NÄR @NumCPUs =1 SÅ 1 NÄR @NumCPUs <=3 SÅ 2 NÄR @NumCPUs>=4 DÅ (VÄLJ MIN(n) FRÅN ( VÄRDEN(@NumCPUs / 2), (MaxDOP4C ) ) SOM D2(n)) END AS DOP4CFROM ( VALUES( CASE WHEN @MemoryMBs <=4096 THEN 8 ELSE 32 END ) ) AS D1(MaxDOP4C);

Med de angivna indatavärdena returnerar denna fråga 4.

Tabell 1 beskriver DOP för kostnad som du får baserat på det logiska antalet processorer och mängden minne i din maskin.

#CPUs DOP för kostnadsberäkning när MemoryMBs <=4096 DOP för kostnad när MemoryMBs> 4096
1 1 1
2-5 2 2
6-7 3 3
8-9 4 4
10-11 5 5
12-13 6 6
14-15 7 7
16-17 8 8
18-19 8 9
20-21 8 10
22-23 8 11
24-25 8 12
26-27 8 13
28-29 8 14
30-31 8 15
32-33 8 16
34-35 8 17
36-37 8 18
38-39 8 19
40-41 8 20
42-43 8 21
44-45 8 22
46-47 8 23
48-49 8 24
50-51 8 25
52-53 8 26
54-55 8 27
56-57 8 28
58-59 8 29
60-61 8 30
62-63 8 31
>=64 8 32

Tabell 1:DOP för kostnadsberäkning

Som ett exempel, låt oss återgå till fråga 1 och fråga 2 som visades tidigare:

-- Fråga 1:Forcerad seriell SELECT custid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY custidOPTION(MAXDOP 1); -- Fråga 2:Naturligtvis parallell SELECT custid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY custid;

Planerna för dessa frågor visas i figur 7.

Figur 7:DOP för kostnad

Fråga 1 tvingar fram en seriell plan, medan fråga 2 får en parallell plan i min miljö (emulerar 8 logiska processorer och 16 384 MB minne). Det betyder att DOP för kostnad i min miljö är 4. Som nämnts beräknas en parallelloperatörs CPU-kostnad som serieoperatörens CPU-kostnad dividerat med DOP för kostnad. Du kan se att det verkligen är fallet i vår parallella plan med operatörerna Index Seek och Hash Aggregate som körs parallellt.

När det gäller kostnaderna för växlingsoperatörerna är de gjorda av en startkostnad och en viss konstant kostnad per rad, som du enkelt kan bakåtkonstruera.

Lägg märke till att i den enkla parallella grupperings- och aggregeringsstrategin, som är den som används här, är kardinalitetsuppskattningarna i serie- och parallellplanerna desamma. Det beror på att endast en samlad operatör är anställd. Senare kommer du att se att saker och ting är annorlunda när du använder den lokala/globala strategin.

Följande frågor hjälper till att illustrera effekten av antalet logiska processorer och antalet inblandade rader på frågekostnaden (10 frågor, med steg om 100 000 rader):

SELECT empid, COUNT(*) AS numordersFROM dbo.OrdersWHERE orderid>=900001GROUP BY empid; SELECT empid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=800001GROUP BY empid; SELECT empid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=700001GROUP BY empid; SELECT empid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=600001GROUP BY empid; VÄLJ empid, COUNT(*) SOM numordersFROM dbo.OrdersWHERE orderid>=500001GROUP BY empid; VÄLJ empid, COUNT(*) SOM numordersFROM dbo.OrdersWHERE orderid>=400001GROUP BY empid; VÄLJ empid, COUNT(*) SOM numordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY empid; VÄLJ empid, COUNT(*) SOM numordersFROM dbo.OrdersWHERE orderid>=200001GROUP BY empid; VÄLJ empid, COUNT(*) SOM numordersFROM dbo.OrdersWHERE orderid>=100001GROUP BY empid; VÄLJ empid, COUNT(*) SOM numordersFROM dbo.OrdersWHERE orderid>=000001GROUP BY empid;

Figur 8 visar resultaten.

Figur 8:Frågekostnad med avseende på #CPU:er och #rader

Den gröna linjen representerar kostnaderna för de olika frågorna (med olika antal rader) med en serieplan. De andra raderna representerar kostnaderna för de parallella planerna med olika antal logiska CPU:er och deras respektive DOP för kostnadsberäkning. Den röda linjen representerar punkten där den seriella frågekostnaden är 5 – standardkostnadströskeln för parallellitetsinställning. Till vänster om denna punkt (färre rader som ska grupperas och aggregeras) kommer normalt inte optimeraren att överväga en parallell plan. För att kunna undersöka kostnaderna för parallella planer under kostnadströskeln för parallellitet kan man göra en av två saker. Ett alternativ är att använda frågetipset ENABLE_PARALLEL_PLAN_PREFERENCE, men som en påminnelse maximerar detta alternativ parallellism i motsats till att bara tvinga fram den. Om det inte är den önskade effekten kan du bara inaktivera kostnadströskeln för parallellitet, så här:

EXEC sp_configure 'visa avancerade alternativ', 1; KONFIGURERA OM; EXEC sp_configure 'kostnadströskel för parallellism', 0; EXEC sp_configure 'visa avancerade alternativ', 0; KONFIGURERA OM;

Uppenbarligen är det inte ett smart drag i ett produktionssystem, men perfekt användbart för forskningsändamål. Det var vad jag gjorde för att producera informationen för diagrammet i figur 8.

Om man börjar med 100K rader och lägger till 100K steg, verkar alla grafer antyda att om kostnadströskeln för parallellitet inte varit en faktor, skulle en parallell plan alltid ha varit att föredra. Det är verkligen fallet med våra frågor och antalet inblandade rader. Testa dock ett mindre antal rader, börja med 10 000 och öka med 10 000 steg med följande fem frågor (återigen, håll kostnadströskeln för parallellitet inaktiverad tills vidare):

SELECT empid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=990001GROUP BY empid; SELECT empid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=980001GROUP BY empid; SELECT empid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=970001GROUP BY empid; SELECT empid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=960001GROUP BY empid; VÄLJ empid, COUNT(*) SOM numordersFROM dbo.OrdersWHERE orderid>=950001GROUP BY empid;

Figur 9 visar frågekostnaderna med både seriella och parallella planer (emulering av 4 processorer, DOP för kostnad 2).

Figur 9:Seriell / parallell plan tröskel

Som du kan se finns det en optimeringströskel upp till vilken serieplanen är att föredra och över vilken den parallella planen är att föredra. Som nämnts, i ett normalt system där du antingen håller kostnadströskeln för parallellitetsinställning på standardvärdet 5 eller högre, är den effektiva tröskeln ändå högre än i denna graf.

Tidigare nämnde jag att när SQL Server väljer den enkla strategin för gruppering och aggregering, så är kardinalitetsuppskattningarna för seriella och parallella planer desamma. Frågan är hur SQL Server hanterar kardinalitetsuppskattningarna för den lokala/globala parallellitetsstrategin.

För att ta reda på detta använder jag fråga 3 och fråga 4 från våra tidigare exempel:

-- Fråga 3:Lokal parallell global parallell SELECT empid, COUNT(*) AS numordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY empid; -- Fråga 4:Lokal parallell global seriell SELECT shipperid, COUNT(*) AS nummordersFROM dbo.OrdersWHERE orderid>=300001GROUP BY shipperid;

I ett system med 8 logiska processorer och en effektiv DOP för ett kostnadsvärde på 4 fick jag planerna som visas i figur 10.

Figur 10:Uppskattning av kardinalitet

Fråga 3 grupperar beställningarna efter empid. 500 distinkta personalgrupper förväntas så småningom.

Fråga 4 grupperar beställningarna efter fraktnummer. Fem distinkta avsändargrupper förväntas så småningom.

Märkligt nog verkar det som om kardinalitetsuppskattningen för antalet grupper som produceras av det lokala aggregatet är { antal distinkta grupper som förväntas av varje tråd } * { DOP för kostnadsberäkning }. I praktiken inser du att antalet vanligtvis blir dubbelt så mycket eftersom det som räknas är DOP för exekvering (aka, bara DOP), som i första hand baseras på antalet logiska CPU:er. Den här delen är lite knepig att emulera för forskningsändamål eftersom kommandot DBCC OPTIMIZER_WHATIF med alternativet CPUs påverkar beräkningen av DOP för kostnadsberäkning, men DOP för exekvering kommer inte att vara större än det faktiska antalet logiska CPU:er som din SQL Server-instans ser. Detta antal är i huvudsak baserat på antalet schemaläggare som SQL Server startar med. Du kan kontrollera antalet schemaläggare SQL Server börjar med att använda -P{ #schedulers } startparameter, men det är ett lite mer aggressivt forskningsverktyg jämfört med ett sessionsalternativ.

Hur som helst, utan att emulera några resurser, har min testmaskin 4 logiska CPU:er, vilket resulterar i DOP för kostnad 2 och DOP för exekvering 4. I min miljö visar det lokala aggregatet i planen för Query 3 en uppskattning av 1 000 resultatgrupper (500 x 2) och en faktisk av 2 000 (500 x 4). På samma sätt visar det lokala aggregatet i planen för fråga 4 en uppskattning av 10 resultatgrupper (5 x 2) och en faktisk av 20 (5 x 4).

När du är klar med experimentet kör du följande kod för rensning:

-- Ställ in kostnadströskeln för parallellism till standard EXEC sp_configure 'visa avancerade alternativ', 1; KONFIGURERA OM; EXEC sp_configure 'kostnadströskel för parallellism', 5; EXEC sp_configure 'visa avancerade alternativ', 0; RECONFIGURE;GO -- Återställ OPTIMIZER_WHATIF alternativ DBCC OPTIMIZER_WHATIF(ResetAll); -- Släpp index DROP INDEX idx_oid_i_sid ON dbo.Orders;DROP INDEX idx_oid_i_eid ON dbo.Orders;DROP INDEX idx_oid_i_cid ON dbo.Orders;

Slutsats

I den här artikeln beskrev jag ett antal parallellitetsstrategier som SQL Server använder för att hantera gruppering och aggregering. Ett viktigt koncept att förstå vid optimering av frågor med parallella planer är graden av parallellism (DOP) för kostnadsberäkning. Jag visade ett antal optimeringströsklar, inklusive ett tröskelvärde mellan seriella och parallella planer, och inställningskostnadströskeln för parallellitet. De flesta av begreppen jag beskrev här är inte unika för gruppering och aggregering, utan är snarare lika tillämpliga för parallella planöverväganden i SQL Server i allmänhet. Nästa månad fortsätter jag serien genom att diskutera optimering med omskrivningar av frågor.


  1. Uppdateringar av JSON-fältet kvarstår inte i DB

  2. Databasdesign för användarinställningar

  3. Uppdatera/Uppdatera en formulärskärm i Oracle D2k Forms 6i

  4. Reguljära uttryck i SQL Server-servrar?