sql >> Databáze >  >> RDS >> Database

Výkon sys.partitions

sys.partitions se zdá být UNION VŠECH dvou sad výsledků (úložiště řádků a úložiště sloupců) a většina mých dotazů vede ke dvěma skenům sad sysrowsetů. Existuje nějaký filtr, který mohu vložit na dotaz sys.partitions, pokud vím, že řádek, který hledám, je rowstore?

Tuto otázku zaslal Jake Manske na #sqlhelp a upozornil mě na ni Erik Darling.

Nevzpomínám si, že bych měl někdy problém s výkonem s sys.partitions . Moje první myšlenka (odezvěna od Joeyho D'Antoniho) byla, že filtr na data_compression sloupec by měl vyhnout se redundantnímu skenování a zkrátit dobu běhu dotazu asi na polovinu. Nicméně, tento predikát se netlačí dolů a důvod, proč to chce trochu rozbalit.

Proč je sys.partitions pomalý?

Pokud se podíváte na definici sys.partitions , je to v podstatě to, co Jake popsal – UNION ALL ze všech oddílů columnstore a rowstore, s TŘI explicitní odkazy na sys.sysrowsets (zkrácený zdroj zde):

VYTVOŘTE ZOBRAZENÍ sys.partitions JAKO S partitions_columnstore(...cols...) AS ( SELECT ...cols..., cmprlevel AS data_compression ... FROM sys.sysrowsets rs OUTER APPLY OpenRowset(TABLE ALUCOUNT, rs .rowsetid, 0, 0, 0) ct-------- *** ^^^^^^^^^^^^^^ *** PŘIPOJIT SE VLEVO sys.syspalvalues ​​cl ... KDE .. . sysconv(bit, rs.status &0x00010000) =1 -- Zvažte pouze základní indexy columnstore ), partitions_rowstore(...cols...) AS ( SELECT ...cols..., cmprlevel AS komprese dat ... FROM sys.sysrowsets rs -------- *** ^^^^^^^^^^^^^^ *** PŘIPOJIT SE VLEVO sys.syspalvalues ​​cl ... WHERE ... sysconv(bit, rs .status &0x00010000) =0 -- Ignorovat základní indexy columnstore a osamocené řádky. ) VYBERTE ...sloupce... z partitions_rowstore p OUTER APPLY OpenRowset(TABLE ALUCOUNT, p.partition_id, 0, 0, p.object_id) ct union all SELECT ...cols... FROM partitions_columnstore as P1 LEVÉ PŘIPOJENÍ (SELECT ...cols... FROM sys.sysrowsets rs OUTER APP LY OpenRowset(TABLE ALUCOUNT, rs.rowsetid, 0, 0, 0) ct------- *** ^^^^^^^^^^^^^^ *** ) ... 

Tento pohled se zdá být dlážděný, pravděpodobně kvůli obavám ze zpětné kompatibility. Určitě by mohl být přepsán, aby byl efektivnější, zejména aby odkazoval pouze na sys.sysrowsets a TABLE ALUCOUNT objekty jednou. Ale vy nebo já s tím teď moc nemůžete udělat.

Sloupec cmprlevel pochází z sys.sysrowsets (předpona aliasu v odkazu na sloupec by byla užitečná). Doufali byste, že predikát proti sloupci tam logicky nastane před jakýmkoli OUTER APPLY a mohlo by zabránit jednomu ze skenování, ale to se nestane. Spuštění následujícího jednoduchého dotazu:

SELECT * FROM sys.partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id WHERE o.is_ms_shipped =0;

Poskytuje následující plán, pokud jsou v databázích indexy columnstore (kliknutím zvětšíte):

Plán pro sys.partitions s indexy columnstore

A následující plán, když tam nejsou (kliknutím zvětšíte):

Plán pro sys.partitions, bez indexů columnstore

Jedná se o stejný odhadovaný plán, ale SentryOne Plan Explorer dokáže zvýraznit, když je operace za běhu přeskočena. To se stane při třetím skenování v druhém případě, ale nevím, že existuje způsob, jak tento počet runtime skenů dále snížit; druhé skenování proběhne, i když dotaz vrátí nula řádků.

V Jakeově případě toho má hodně objektů, takže provedení tohoto skenování i dvakrát je znatelné, bolestivé a jednou příliš mnoho. A upřímně řečeno nevím, jestli TABLE ALUCOUNT , interní a nezdokumentované volání zpětné smyčky, musí také několikrát skenovat některé z těchto větších objektů.

Když jsem se podíval zpět na zdroj, přemýšlel jsem, zda existuje nějaký jiný predikát, který by mohl být předán pohledu, který by mohl vynutit tvar plánu, ale opravdu si nemyslím, že by tam bylo něco, co by mohlo mít vliv.

Bude fungovat jiný pohled?

Mohli bychom však zkusit úplně jiný pohled. Hledal jsem další pohledy, které obsahovaly odkazy na obě sys.sysrowsets a ALUCOUNT a v seznamu je jich několik, ale pouze dva jsou slibné:sys.internal_partitions a sys.system_internals_partitions .

sys.internal_partitions

Zkoušel jsem sys.internal_partitions první:

SELECT * FROM sys.internal_partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id WHERE o.is_ms_shipped =0;

Ale plán nebyl o moc lepší (klikněte pro zvětšení):

Plán pro sys.internal_partitions

Existují pouze dvě kontroly proti sys.sysrowsets tentokrát, ale skeny jsou stejně irelevantní, protože dotaz se neblíží k vytvoření řádků, které nás zajímají. Vidíme pouze řádky pro objekty související s columnstore (jak uvádí dokumentace).

sys.system_internals_partitions

Zkusme sys.system_internals_partitions . Jsem v tom trochu opatrný, protože to není podporováno (viz varování zde), ale mějte chvíli se mnou:

SELECT * FROM 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;

V databázi s indexy columnstore je skenování proti sys.sysschobjs , ale nyní pouze jeden skenovat proti sys.sysrowsets (kliknutím zvětšíte):

Plán pro sys.system_internals_partitions s indexy columnstore

Pokud spustíme stejný dotaz v databázi bez indexů columnstore, plán je ještě jednodušší, s hledáním proti sys.sysschobjs (kliknutím zvětšíte):

Plán pro sys.system_internals_partitions, bez indexů columnstore

To však není úplně o co nám jde, nebo alespoň ne tak úplně o to, po čem Jake šel, protože zahrnuje i artefakty z indexů columnstore. Pokud přidáme tyto filtry, skutečný výstup nyní odpovídá našemu dřívějšímu, mnohem dražšímu dotazu:

SELECT * FROM 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 AND p.is_columnstore =0 AND p.is_orphaned =0;

Jako bonus skenování proti sys.sysschobjs se stalo vyhledáváním i v databázi s objekty columnstore. Většina z nás si toho rozdílu nevšimne, ale pokud jste ve scénáři jako Jake, můžete (kliknutím zvětšíte):

Jednodušší plán pro sys.system_internals_partitions s dalšími filtry

sys.system_internals_partitions odhaluje jinou sadu sloupců než sys.partitions (některé jsou zcela odlišné, jiné mají nová jména), takže pokud spotřebováváte výstup downstream, budete se muset přizpůsobit. Budete také chtít ověřit, že vrací všechny požadované informace napříč indexy rowstore, paměti optimalizované a columnstore, a nezapomeňte na ty otravné hromady. A nakonec buďte připraveni vynechat s v internals mnohokrát, mnohokrát.

Závěr

Jak jsem uvedl výše, tento systémový pohled není oficiálně podporován, takže jeho funkčnost se může kdykoli změnit; lze jej také přesunout pod připojení Dedicated Administrator Connection (DAC) nebo jej z produktu zcela odebrat. Neváhejte použít tento přístup, pokud sys.partitions nefunguje pro vás dobře, ale ujistěte se, že máte záložní plán. A ujistěte se, že je to zdokumentováno jako něco, co regresně testujete, když začnete testovat budoucí verze SQL Serveru, nebo po upgradu, pro každý případ.


  1. Alternativa k použití klíčového slova LIMIT v poddotazu v MYSQL

  2. Jak najít kolace databáze podporované vaší instancí SQL Server

  3. 5 způsobů, jak najít řádky, které obsahují velká písmena na serveru SQL

  4. Připojení k MySQL z Androidu pomocí JDBC