sql >> Databasteknik >  >> RDS >> Database

Prestanda för sys.partitioner

sys.partitions verkar vara en UNION ALLA två resultatuppsättningar (radlager och kolumnlager) och de flesta av mina frågor resulterar i två skanningar av sysrowset. Finns det något filter jag kan sätta på en fråga av sys.partitions om jag vet att raden jag letar efter är rowstore?

Den här frågan postades till #sqlhelp av Jake Manske, och den uppmärksammades av Erik Darling.

Jag kan inte minnas att jag någonsin haft ett prestandaproblem med sys.partitions . Min första tanke (som upprepades av Joey D'Antoni) var att ett filter på data_compression kolumnen bör undvik den redundanta skanningen och minska söktiden med ungefär hälften. Det här predikatet trycks dock inte ner, och anledningen till det kräver lite uppackning.

Varför är sys.partitions långsam?

Om du tittar på definitionen för sys.partitions , det är i princip vad Jake beskrev – en UNION ALL av alla columnstore- och rowstore-partitioner, med TRE explicita referenser till sys.sysrowsets (förkortad källa här):

SKAPA VISNING sys.partitions AS WITH partitions_columnstore(...cols...) AS ( VÄLJ ...cols..., cmprlevel AS data_compression ... FRÅN sys.sysrowsets rs YTTRE ANVÄND OpenRowset(TABELL ALUCOUNT, rs .rowsetid, 0, 0, 0) ct-------- *** ^^^^^^^^^^^^^^ *** VÄNSTER JOIN sys.syspalvalues ​​cl ... VAR .. . sysconv(bit, rs.status &0x00010000) =1 -- Tänk bara på kolumnlagerbasindex ), partitions_rowstore(...cols...) AS ( SELECT ...cols..., cmprlevel AS data_compression ... FROM sys.sysrowsets rs -------- *** ^^^^^^^^^^^^^^^ *** VÄNSTER JOIN sys.syspalvalues ​​cl ... WHERE ... sysconv(bit, rs .status &0x00010000) =0 -- Ignorera kolumnlagerbasindex och föräldralösa rader. ) VÄLJ ...kol... från partitions_rowstore p OUTTER APPLY OpenRowset(TABELL ALUCOUNT, p.partition_id, 0, 0, p.object_id) ct union alla SELECT ...cols... FROM partitions_columnstore som P1 LEFT JOIN (VÄLJ ...cols... FRÅN sys.sysrowsets rs YTTRE APPEN LY OpenRowset(TABELL ALUCOUNT, rs.rowsetid, 0, 0, 0) ct------ *** ^^^^^^^^^^^^^^ *** ) ... 

Den här uppfattningen tycks vara sammansatt, förmodligen på grund av bakåtkompatibilitetsproblem. Det skulle säkert kunna skrivas om för att bli mer effektivt, särskilt för att bara referera till sys.sysrowsets och TABLE ALUCOUNT föremål en gång. Men det finns inte mycket du eller jag kan göra åt det just nu.

Kolumnen cmprlevel kommer från sys.sysrowsets (ett aliasprefix på kolumnreferensen skulle ha varit till hjälp). Du skulle hoppas att ett predikat mot en kolumn där logiskt sett skulle hända innan någon OUTER APPLY och skulle kunna förhindra en av skanningarna, men det är inte vad som händer. Kör följande enkla fråga:

VÄLJ * FRÅN sys.partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id WHERE o.is_ms_shipped =0;

Ger följande plan när det finns kolumnlagerindex i databaserna (klicka för att förstora):

Planera för sys.partitions, med columnstore-index närvarande

Och följande plan när det inte finns (klicka för att förstora):

Planera för sys.partitions, utan kolumnlagerindex närvarande

Dessa är samma beräknade plan, men SentryOne Plan Explorer kan markera när en operation hoppas över under körning. Detta händer för den tredje skanningen i det senare fallet, men jag vet inte att det finns något sätt att minska antalet runtime-skanningar ytterligare; den andra skanningen sker även när frågan returnerar noll rader.

I Jakes fall har han mycket av objekt, så att utföra denna skanning ens två gånger är märkbart, smärtsamt och en gång för mycket. Och ärligt talat vet jag inte om TABLE ALUCOUNT , ett internt och odokumenterat loopback-anrop, måste också skanna några av dessa större objekt flera gånger.

När jag tittade tillbaka på källan undrade jag om det fanns något annat predikat som skulle kunna överföras till åsikten som skulle kunna tvinga fram planformen, men jag tror verkligen inte att det finns något som kan påverka.

Kommer en annan vy att fungera?

Vi skulle dock kunna prova en helt annan syn. Jag letade efter andra vyer som innehöll referenser till båda sys.sysrowsets och ALUCOUNT , och det finns flera som dyker upp i listan, men bara två är lovande:sys.internal_partitions och sys.system_internals_partitions .

sys.internal_partitions

Jag försökte sys.internal_partitions först:

VÄLJ * FRÅN sys.internal_partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id WHERE o.is_ms_shipped =0;

Men planen var inte mycket bättre (klicka för att förstora):

Planera för sys.internal_partitions

Det finns bara två skanningar mot sys.sysrowsets denna gång, men skanningarna är ändå irrelevanta eftersom frågan inte kommer i närheten av att producera de rader vi är intresserade av. Vi ser bara rader för columnstore-relaterade objekt (som dokumentationen anger).

sys.system_internals_partitions

Låt oss prova sys.system_internals_partitions . Jag är lite försiktig med detta, eftersom det inte stöds (se varningen här), men tål mig ett ögonblick:

VÄLJ * FRÅN sys.system_internals_partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id WHERE o.is_ms_shipped =0;

I databasen med kolumnlagerindex finns en skanning mot sys.sysschobjs , men nu bara en skanna mot sys.sysrowsets (klicka för att förstora):

Planera för sys.system_internals_partitions, med columnstore-index närvarande

Om vi ​​kör samma fråga i databasen utan kolumnlagerindex är planen ännu enklare, med en sökning mot sys.sysschobjs (klicka för att förstora):

Planera för sys.system_internals_partitions, utan kolumnlagerindex närvarande

Detta är dock inte helt vad vi är ute efter, eller åtminstone inte riktigt vad Jake var ute efter, eftersom det också inkluderar artefakter från kolumnbutiksindex. Om vi ​​lägger till dessa filter, matchar den faktiska utdata nu vår tidigare, mycket dyrare fråga:

VÄLJ * FRÅN sys.system_internals_partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id WHERE o.is_ms_shipped =0 OCH p.is_columnstore =0 OCH p.is_orphaned =0;

Som en bonus, skanningen mot sys.sysschobjs har blivit ett sök även i databasen med columnstore-objekt. De flesta av oss kommer inte att märka den skillnaden, men om du befinner dig i ett scenario som Jakes, kan du bara (klicka för att förstora):

Enklare plan för sys.system_internals_partitions, med ytterligare filter

sys.system_internals_partitions exponerar en annan uppsättning kolumner än sys.partitions (vissa är helt annorlunda, andra har nya namn) så om du konsumerar utdata nedströms måste du anpassa dig efter dem. Du vill också verifiera att den returnerar all information du vill ha över index för rowstore, minnesoptimerade och columnstore, och glöm inte de där irriterande högarna. Och slutligen, var redo att utelämna s i internals många, många gånger.

Slutsats

Som jag nämnde ovan stöds inte denna systemvy officiellt, så dess funktionalitet kan ändras när som helst; den kan också flyttas under DAC (Dedicated Administrator Connection), eller tas bort från produkten helt och hållet. Använd gärna denna metod om sys.partitions fungerar inte bra för dig, men se till att du har en reservplan. Och se till att det är dokumenterat som något du regressionstestar när du börjar testa framtida versioner av SQL Server, eller efter att du har uppgraderat, för säkerhets skull.


  1. SET TEXTSTORLEK Fungerar inte i SQL Server? Kontrollera detta.

  2. Hur man får åtkomst till MySQL med MySQL Root User

  3. Hur man importerar/återställer MySql-tabeller med PHP

  4. Hur använder man strftime och datetime korrekt med Room library?