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
aTABLE ALUCOUNT
objekty jednou. Ale vy nebo já s tím teď moc nemůžete udělat.Sloupec
cmprlevel
pochází zsys.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ýmkoliOUTER 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
aALUCOUNT
a v seznamu je jich několik, ale pouze dva jsou slibné:sys.internal_partitions
asys.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 protisys.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 vynechats
vinternals
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.