Pochopení nákladů Postgres EXPLAIN
EXPLAIN
je velmi užitečné pro pochopení výkonu dotazu Postgres. Vrací plán provádění vygenerovaný plánovačem dotazů PostgreSQL pro daný příkaz. EXPLAIN
příkaz určuje, zda budou tabulky odkazované v příkazu prohledávány pomocí indexového skenování nebo sekvenčního skenování.
Některé z prvních věcí, kterých si všimnete při kontrole výstupu EXPLAIN
jsou statistiky nákladů, takže je přirozené přemýšlet, co znamenají, jak se počítají a jak se používají.
Stručně řečeno, plánovač dotazů PostgreSQL odhaduje, jak dlouho dotaz zabere (v libovolné jednotce), jak s počátečními náklady, tak s celkovými náklady na každou operaci. Více o tom později. Když má více možností pro provedení dotazu, použije tyto náklady k výběru nejlevnější, a proto doufejme, že nejrychlejší možnosti.
V jakých jednotkách jsou náklady?
Náklady jsou v libovolné jednotce. Běžným nedorozuměním je, že jsou v milisekundách nebo jiné časové jednotce, ale není tomu tak.
Jednotky nákladů jsou ukotveny (ve výchozím nastavení) k jednomu sekvenčnímu přečtení stránky za cenu 1,0 jednotky (seq_page_cost
). Každý zpracovaný řádek přidá 0,01 (cpu_tuple_cost
) a každé další čtení stránky přidá 4,0 (random_page_cost
). Podobných konstant je mnohem více, všechny jsou konfigurovatelné. Ten poslední je obzvláště častým kandidátem, alespoň na moderním hardwaru. Za chvíli se na to podíváme více.
Počáteční náklady
První čísla, která uvidíte za cost=
jsou známé jako „počáteční náklady“. Toto je odhad, jak dlouho bude trvat načtení prvního řádku . Spouštěcí náklady operace jako takové zahrnují náklady na její potomky.
U sekvenčního skenování se spouštěcí náklady budou obecně blížit nule, protože může začít načítat řádky okamžitě. U operace řazení bude vyšší, protože je třeba provést velkou část práce, než se mohou začít vracet řádky.
Abychom se podívali na příklad, vytvoříme jednoduchou testovací tabulku s 1000 uživatelskými jmény:
CREATE TABLE users ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, text username NOT NULL);INSERT INTO users (username)SELECT 'person' || nFROM create_series(1, 1000) AS n;ANALYZE users;
Podívejme se na jednoduchý plán dotazů s několika operacemi:
EXPLAIN SELECT * OD uživatelů OBJEDNAT PODLE uživatelského jména;PLÁNU DOTAZŮ |----------------------------------- ---------------------------+Třídit (cena=66,83..69,33 řádků=1000 šířka=17) | Klíč řazení:uživatelské jméno | -> Seq Scan na uživatele (cena=0,00..17,00 řádků=1000 šířka=17)|
Ve výše uvedeném plánu dotazů jsou podle očekávání odhadované náklady na provedení příkazu pro Seq Scan
je 0.00
a pro Sort
je 66.83
.
Celkové náklady
Druhá statistika nákladů, po počátečních nákladech a dvou tečkách, je známá jako „celkové náklady“. Toto je odhad, jak dlouho bude trvat vrácení všech řádků .
Podívejme se znovu na tento příklad plánu dotazů:
PLÁN DOTAZŮ |-------------------------------------------- ------------------+Třídit (cena=66,83..69,33 řádků=1000 šířka=17) | Klíč řazení:uživatelské jméno | -> Seq Scan na uživatele (cena=0,00..17,00 řádků=1000 šířka=17)|
Vidíme, že celkové náklady na Seq Scan
operace je 17.00
. Pro Sort
provoz je 69,33, což není o mnoho více než jeho počáteční náklady (jak se očekávalo).
Celkové náklady obvykle zahrnují náklady na operace, které jim předcházely. Například celkové náklady na operaci Sort výše zahrnují náklady na Seq Scan.
Důležitou výjimkou je LIMIT
klauzule, které plánovač používá k odhadu, zda lze předčasně zrušit. Pokud potřebuje pouze malý počet řádků, jejichž podmínky jsou běžné, může vypočítat, že jednodušší volba skenování je levnější (pravděpodobně rychlejší).
Například:
EXPLAIN SELECT * FROM Users LIMIT 1; --------------------------+Limit (cena=0,00..0,02 řádků=1 šířka=17) | -> Seq Scan na uživatele (cena=0,00..17,00 řádků=1000 šířka=17)|
Jak můžete vidět, celkové náklady hlášené na uzlu Seq Scan jsou stále 17,00, ale plné náklady na operaci Limit jsou hlášeny jako 0,02. Důvodem je to, že plánovač očekává, že bude muset zpracovat pouze 1 řádek z 1000, takže náklady se v tomto případě odhadují na 1000 z celkových nákladů.
Jak se počítají náklady
K výpočtu těchto nákladů používá plánovač dotazů Postgres jak konstanty (některé jsme již viděli), tak metadata o obsahu databáze. Metadata jsou často označována jako „statistika“.
Statistiky se shromažďují pomocí ANALYZE
(neplést s EXPLAIN
stejnojmenný parametr) a uloženy v pg_statistic
. Obnovují se také automaticky v rámci automatického vakuování.
Tyto statistiky zahrnují řadu velmi užitečných věcí, jako je zhruba počet řádků, které má každá tabulka, a jaké jsou nejběžnější hodnoty v jednotlivých sloupcích.
Podívejme se na jednoduchý příklad pomocí stejných dat dotazu jako dříve:
EXPLAIN SELECT count(*) FROM users;QUERY PLAN |----------------------------------- --------------------------+Agregát (cena=19,50..19,51 řádků=1 šířka=8) | -> Seq Scan na uživatele (cena=0,00..17,00 řádků=1000 šířka=0)|
V našem případě statistiky plánovače naznačovaly, že data pro tabulku byla uložena do 7 stránek (nebo bloků) a že by bylo vráceno 1000 řádků. Parametry nákladů seq_page_cost
, cpu_tuple_cost
a cpu_operator_cost
byly ponechány na výchozí hodnotě 1
, 0.01
a 0.0025
respektive.
Jako takové byly celkové náklady Seq Scan vypočteny takto:
Celkové náklady na Seq Scan=(odhadovaná sekvenční přečtení stránky * seq_page_cost) + (odhadované vrácené řádky * cpu_tuple_cost)=(7 * 1) + (1000 * 0,01)=7 + 10,00=17,00
A pro Agregát jako:
Celkové náklady na Aggregate=(náklady na Seq Scan) + (odhadované zpracované řádky * cpu_operator_cost) + (odhadované vrácené řádky * cpu_tuple_cost)=(17,00) + (1000 * 0,0025) + (1 * 0,01) =27,500 + 17,00 + 0,01=19,51
Jak plánovač využívá náklady
Protože víme, že Postgres vybere plán dotazů s nejnižšími celkovými náklady, můžeme to použít, abychom se pokusili porozumět volbám, které učinil. Pokud například dotaz nepoužívá index, který očekáváte, můžete použít nastavení jako enable_seqscan
k masivnímu odrazování od určitých voleb plánu dotazů. V tuto chvíli by vás nemělo překvapit, že nastavení, jako je toto, zvyšují náklady!
Čísla řádků jsou extrémně důležitou součástí odhadu nákladů. Používají se k výpočtu odhadů pro různé objednávky spojení, algoritmy spojení, typy skenování a další. Odhady nákladů na řadu, které jsou hodně mimo, mohou vést k tomu, že odhady nákladů jsou hodně mimo, což může v konečném důsledku vést k výběru neoptimálního plánu.
Pomocí EXPLAIN ANALYZE získáte plán dotazů
Když píšete SQL příkazy v PostgreSQL, ANALYZE
Příkaz je klíčem k optimalizaci dotazů, díky nimž jsou rychlejší a efektivnější. Kromě zobrazení plánu dotazů a odhadů PostgreSQL, EXPLAIN ANALYZE
volba provede dotaz (buďte opatrní s UPDATE
a DELETE
!) a zobrazuje skutečný čas provádění a počet řádků pro každý krok procesu provádění. To je nezbytné pro monitorování výkonu SQL.
Můžete použít EXPLAIN ANALYZE
porovnat odhadovaný počet řádků se skutečnými řádky vrácenými každou operací.
Podívejme se na příklad a znovu použijeme stejná data:
PLÁN DOTAZŮ |-------------------------------------------- -------------------------------------------------- -------------+Třídit (cena=66,83..69,33 řádků=1000 šířka=17) (skutečný čas=20,569..20,684 řádků=1000 smyček=1) | Klíč řazení:uživatelské jméno | Způsob řazení:rychlé třídění Paměť:102 kB | -> Seq Scan u uživatelů (cena=0,00..17,00 řádků=1000 šířka=17) (skutečný čas=0,048..0,596 řádků=1000 smyček=1)|Čas plánování:0,171 ms |Doba provedení:20,793 ms | před>Vidíme, že celkové náklady na provedení jsou stále 69,33, přičemž většinu tvoří operace řazení a 17,00 pochází ze sekvenčního skenování. Všimněte si, že doba provádění dotazu je těsně pod 21 ms.
Sekvenční prohledávání vs. Indexové prohledávání
Nyní přidejte index, abychom se pokusili vyhnout se tomuto nákladnému druhu celé tabulky:
CREATE INDEX people_username_idx ON users (username);EXPLAIN ANALYZE SELECT * FROM users ORDER BY username;QUERY PLAN |------------------------ -------------------------------------------------- -------------------------------------------------- ------+Skenování indexu pomocí people_username_idx u uživatelů (cena=0,28..28,27 řádků=1000 šířka=17) (skutečný čas=0,052..1,494 řádků=1000 smyček=1)|Čas plánování:0,186 ms |Provedení Čas:1,686 ms |Jak můžete vidět, plánovač dotazů nyní zvolil Index Scan, protože celkové náklady na tento plán jsou 28,27 (nižší než 69,33). Zdá se, že indexové skenování bylo efektivnější než sekvenční skenování, protože doba provedení dotazu je nyní těsně pod 2 ms.
Pomáhá plánovači odhadovat přesněji
Plánovači můžeme pomoci k přesnějšímu odhadu dvěma způsoby:
- Pomozte mu získat lepší statistiky
- Vylaďte konstanty, které používá pro výpočty
Statistiky mohou být obzvláště špatné po velké změně dat v tabulce. Při načítání velkého množství dat do tabulky můžete Postgresu pomoci spuštěním ručního
ANALYZE
na to. Statistiky také nezůstávají po aktualizaci hlavní verze, takže to je další důležitý okamžik, kdy to udělat.Tabulky se přirozeně také v průběhu času mění, takže vyladění nastavení autovakua, aby bylo zajištěno, že bude spouštět dostatečně často pro vaši pracovní zátěž, může být velmi užitečné.
Pokud máte potíže se špatnými odhady pro sloupec s vychýlenou distribucí, můžete mít prospěch ze zvýšení množství informací, které Postgres shromažďuje, pomocí
ALTER TABLE SET STATISTICS
nebo dokoncedefault_statistics_target
pro celou databázi.Další častou příčinou špatných odhadů je, že ve výchozím nastavení bude Postgres předpokládat, že dva sloupce jsou nezávislé. Můžete to opravit tak, že jej požádáte, aby shromáždil korelační data ve dvou sloupcích ze stejné tabulky prostřednictvím rozšířených statistik.
Na frontě neustálého ladění existuje spousta parametrů, které můžete vyladit tak, aby vyhovovaly vašemu hardwaru. Za předpokladu, že používáte disky SSD, pravděpodobně budete chtít minimálně vyladit nastavení
random_page_cost
. Toto výchozí nastavení je 4, což je 4x dražší nežseq_page_cost
jsme se dívali dříve. Tento poměr dával smysl na rotujících discích, ale na SSD má tendenci příliš penalizovat náhodné I/O. Takové nastavení blíže k 1 nebo mezi 1 a 2 by mohlo dávat větší smysl. Ve ScaleGrid je výchozí hodnota 1.Mohu z plánů dotazů odstranit náklady?
Z mnoha výše uvedených důvodů většina lidí nechává náklady na spuštění
EXPLAIN
. Pokud si však přejete, můžete je vypnout pomocíCOSTS
parametr.VYSVĚTLIT (SNÍŽENÉ NÁKLADY) VYBRAT * OD uživatelů LIMIT 1;PLÁNU DOTAZŮ |-----------------------+Limit | -> Seq Scan u uživatelů|Závěr
Abychom to zrekapitulovali, náklady v plánech dotazů jsou odhady Postgresu, jak dlouho bude SQL dotaz trvat, v libovolné jednotce.
Vybere plán s nejnižšími celkovými náklady na základě některých konfigurovatelných konstant a některých statistik, které shromáždil.
Pomoci společnosti odhadnout tyto náklady přesněji je velmi důležité, aby se mohla správně rozhodovat a udržet výkon vašich dotazů.
|