sql >> Database teknologi >  >> RDS >> Database

Ydelse af sys.partitioner

sys.partitions ser ud til at være en UNION ALLE to resultatsæt (rækkelager og kolonnelager), og de fleste af mine forespørgsler resulterer i to scanninger af sysrowsets. Er der et filter, jeg kan sætte på en forespørgsel på sys.partitions, hvis jeg ved, at den række, jeg leder efter, er rowstore?

Dette spørgsmål blev sendt til #sqlhelp af Jake Manske, og det blev gjort mig opmærksom på af Erik Darling.

Jeg kan ikke huske, at jeg nogensinde har haft et ydeevneproblem med sys.partitions . Min første tanke (som gentaget af Joey D'Antoni) var, at et filter på data_compression kolonne skal undgå den overflødige scanning, og reducer forespørgselskørselstiden med cirka det halve. Dette prædikat bliver dog ikke presset ned, og grunden til det kræver lidt udpakning.

Hvorfor er sys.partitions langsomme?

Hvis du ser på definitionen for sys.partitions , det er dybest set, hvad Jake beskrev – en UNION ALL af alle columnstore- og rowstore-partitionerne med TRE eksplicitte referencer til sys.sysrowsets (forkortet kilde her):

CREATE VIEW sys.partitions AS WITH partitions_columnstore(...cols...) AS (VÆLG ...cols..., cmprlevel AS data_compression ... FRA sys.sysrowsets rs YDRE ANVEND OpenRowset(TABEL ALUCOUNT, rs) .rowsetid, 0, 0, 0) ct-------- *** ^^^^^^^^^^^^^^ *** VENSTRE JOIN sys.syspalvalues ​​cl ... HVOR .. . sysconv(bit, rs.status &0x00010000) =1 -- Overvej kun kolonnelagerbaseindekser ), partitions_rowstore(...cols...) AS ( SELECT ...cols..., cmprlevel AS data_compression ... FROM sys.sysrowsets rs -------- *** ^^^^^^^^^^^^^^^ *** VENSTRE JOIN sys.syspalvalues ​​cl ... HVOR ... sysconv(bit, rs .status &0x00010000) =0 -- Ignorer kolonnelagerbaseindekser og forældreløse rækker. ) VÆLG ...cols... fra partitions_rowstore p YDRE ANVEND OpenRowset(TABEL ALUCOUNT, p.partition_id, 0, 0, p.object_id) ct union alle SELECT ...cols... FRA partitions_columnstore som P1 LEFT JOIN (VÆLG ...cols... FRA sys.sysrowsets rs YDRE APP LY OpenRowset(TABEL ALUCOUNT, rs.rowsetid, 0, 0, 0) ct------ *** ^^^^^^^^^^^^^^ *** ) ... 

Dette synspunkt virker brostenset sammen, sandsynligvis på grund af bagudkompatibilitetsproblemer. Det kunne helt sikkert omskrives for at være mere effektivt, især til kun at henvise til sys.sysrowsets og TABLE ALUCOUNT genstande én gang. Men der er ikke meget, du eller jeg kan gøre ved det lige nu.

Kolonnen cmprlevel kommer fra sys.sysrowsets (et aliaspræfiks på kolonnehenvisningen ville have været nyttigt). Du ville håbe, at et prædikat mod en kolonne der logisk ville ske før nogen OUTER APPLY og kunne forhindre en af ​​scanningerne, men det er ikke det, der sker. Kører følgende simple forespørgsel:

VÆLG * FRA sys.partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id WHERE o.is_ms_shipped =0;

Giver følgende plan, når der er kolonnelagerindekser i databaserne (klik for at forstørre):

Plan for sys.partitions, med kolonnelagerindekser til stede

Og følgende plan, når der ikke er (klik for at forstørre):

Plan for sys.partitions, uden kolonnelagerindekser til stede

Disse er den samme estimerede plan, men SentryOne Plan Explorer er i stand til at fremhæve, når en operation springes over under kørsel. Dette sker for den tredje scanning i sidstnævnte tilfælde, men jeg ved ikke, at der er nogen måde at reducere antallet af runtime-scanninger yderligere; den anden scanning sker, selv når forespørgslen returnerer nul rækker.

I Jakes tilfælde har han en masse af genstande, så at udføre denne scanning endda to gange er mærkbar, smertefuld og én gang for meget. Og helt ærligt ved jeg ikke om TABLE ALUCOUNT , et internt og udokumenteret loopback-kald, skal også scanne nogle af disse større objekter flere gange.

Når jeg så tilbage på kilden, spekulerede jeg på, om der var noget andet prædikat, der kunne overføres til synspunktet, der kunne tvinge planens form, men jeg tror virkelig ikke, der er noget, der kunne have en indflydelse.

Vil en anden visning fungere?

Vi kunne dog prøve en helt anden opfattelse. Jeg ledte efter andre visninger, der indeholdt referencer til begge sys.sysrowsets og ALUCOUNT , og der er flere, der dukker op på listen, men kun to er lovende:sys.internal_partitions og sys.system_internals_partitions .

sys.internal_partitions

Jeg prøvede sys.internal_partitions først:

VÆLG * FRA 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 ikke meget bedre (klik for at forstørre):

Plan for sys.internal_partitions

Der er kun to scanninger mod sys.sysrowsets denne gang, men scanningerne er alligevel irrelevante, fordi forespørgslen ikke kommer i nærheden af ​​at producere de rækker, vi er interesserede i. Vi ser kun rækker for columnstore-relaterede objekter (som det fremgår af dokumentationen).

sys.system_internals_partitions

Lad os prøve sys.system_internals_partitions . Jeg er lidt forsigtig med dette, fordi det ikke understøttes (se advarslen her), men bær over med mig et øjeblik:

VÆLG * FRA 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 kolonnelagerindekser er der en scanning mod sys.sysschobjs , men nu kun én scan mod sys.sysrowsets (klik for at forstørre):

Plan for sys.system_internals_partitions, med kolonnelagerindekser til stede

Hvis vi kører den samme forespørgsel i databasen uden kolonnelagerindekser, er planen endnu enklere, med en søgning mod sys.sysschobjs (klik for at forstørre):

Plan for sys.system_internals_partitions, uden kolonnelagerindekser til stede

Dette er dog ikke helt hvad vi leder efter, eller i det mindste ikke helt hvad Jake var ude efter, fordi det også inkluderer artefakter fra columnstore-indekser. Hvis vi tilføjer disse filtre, svarer det faktiske output nu til vores tidligere, meget dyrere forespørgsel:

VÆLG * FRA sys.system_internals_partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id HVOR o.is_ms_shipped =0 OG p.is_columnstore =0 OG p.is_orphaned =0;

Som en bonus kan scanningen mod sys.sysschobjs er blevet en søgning selv i databasen med columnstore-objekter. De fleste af os vil ikke bemærke den forskel, men hvis du er i et scenarie som Jakes, kan du bare (klik for at forstørre):

Enklere plan for sys.system_internals_partitions, med yderligere filtre

sys.system_internals_partitions viser et andet sæt kolonner end sys.partitions (nogle er helt anderledes, andre har nye navne), så hvis du forbruger output downstream, bliver du nødt til at justere for dem. Du vil også gerne bekræfte, at den returnerer al den information, du ønsker på tværs af rowstore, hukommelsesoptimerede og columnstore-indekser, og glem ikke de irriterende dynger. Og vær endelig klar til at udelade s i internals mange, mange gange.

Konklusion

Som jeg nævnte ovenfor, er denne systemvisning ikke officielt understøttet, så dens funktionalitet kan ændres til enhver tid; den kan også flyttes under DAC (Dedicated Administrator Connection) eller fjernes helt fra produktet. Du er velkommen til at bruge denne tilgang, hvis sys.partitions fungerer ikke godt for dig, men sørg venligst for at have en backup-plan. Og sørg for, at det er dokumenteret som noget, du regressionstester, når du begynder at teste fremtidige versioner af SQL Server, eller efter du har opgraderet, for en sikkerheds skyld.


  1. Skift separatoren til et komma i SQLite-forespørgselsresultater

  2. visning af flere poster ved hjælp af resultatsæt

  3. ugyldig bytesekvens til kodning af UTF8

  4. java.lang.IllegalArgumentException:kolonne '_id' findes ikke