Spracovanie dotazov v PostgreSQL Tomáš Kovařík MFF UK, Praha kovo@matfyz.cz
Osnova Úvod Ciele a význam prezentácie Architektúra PostgreSQL Štruktúra procesov a komunikácia medzi procesmi Štruktúra backendu Fáze spracovania SQL dotazov Parser - Načítanie a analýza dotazu Rewriter - Transformácia dotazu podľa pravidiel Planner - Výber plánu vyhodnotenia Používanie štatistík Optimalizácia poradia joinov Plánovanie outer joinov a poddotazov Executor - Vykonanie dotazu Záver a otázky 2 / 20
Úvod Ciele Zoznámenie sa s osudom dotazu od jeho zadania až po získanie výsledkov Pochopenie architektúry a štruktúry jadra Postgresu Význam Základ pre zlepšovanie výkonnosti Oprava chýb a ladenie dotazov Objavenie elegancie a prepracovanosti Postgresu 3 / 20
Architektúra PostgreSQL Architektúra klient / server Klient sa pripája k serveru prostredníctvom TCP/IP alebo unixových socketov Komunikácia pomocou PostgreSQL protokolu Proces naviazania spojenia Klient sa pripojí ku hlavnému procesu postmaster Na serveri sa vytvorí nový proces (fork) ktorý ďalej obsluhuje klienta Cyklus vykonávania dotazov - zadanie dotazu, vrátenie výsledku Výhody Oddelenie procesov zlepšuje bezpečnosť a stabilitu Nevýhody Komunikácia procesov cez zdieľanú pamäť, škálovateľnosť 4 / 20
Architektúra PostgreSQL 5 / 20
Štruktúra backendu Vstup - SQL dotaz v textovej podobe Výstup - Dáta, výsledok dotazu (podľa typu dotazu) Jednotlivé kroky: Parser - Načítanie a analýza dotazu Rewriter - Transformácia dotazu podľa pravidiel Planner / Optimizer - Výber plánu vyhodnotenia Executor - Vykonanie dotazu Metadáta - systémové katalógy Dátové štruktúry Syntaktický strom (parse tree) Strom dotazu (query tree) Plánovací strom (plan tree) 6 / 20
Parser Rozbor zadaného SQL dotazu Transformácia na dátové štruktúry Syntaktická a sémantická analýza dotazu Rozdelený na dve časti Rozbor - syntaktická kontrola dotazu bez prístupu do DB Transformácia - sémantická analýza, načítanie metadát z DB Výstupom prvej časti je syntaktický strom Výstupom druhej časti je strom dotazu Syntaktický strom doplnený o informácie o tabuľkách, funkciách, operátoroch... Dôvodom oddelenia je spracovanie kontrolných príkazov bez prístupu k databázi 7 / 20
Parser - príklad Vstup: SELECT * FROM test1, test2 WHERE test1.col1 = test2.col2 Výstup: 8 / 20
Rewriter Systém pravidiel pre modifikáciu dotazu Funkcionalita podobná ako u uložených procedúr a triggerov ale konceptuálne úplne odlišné Pravidlá uložene v katalógu pg_rewrite Vstupom aj výstupom je strom dotazu, na výstupe môže byť stromov viac (alebo žiaden) ON SELECT pravidlá Ošetrenie pohľadov (views), substitúcia poddotazu (pohľadu) do stromu dotazu ON INSERT/UPDATE/DELETE pravidlá Nemodifikujú pôvodný dotaz (môžu ho vynechať) Pridávajú ďalšie dotazy, resp. stromy dotazu 9 / 20
Planner / Optimizer Máme načítaný a predzpracovaný dotaz Syntax, metadáta, transformácie... Potrebujeme vybrať spôsob ako dotaz vykonáme a vykonať ho Výber najlepšieho spôsobu (plánu) ako daný dotaz vykonať je úlohou optimizera Táto úloha sa rozpadá na dve časti: Vygenerovanie všetkých vhodných plánov Výber najlepšieho plánu pre vykonanie dotazu Plán je reprezentovaný stromom Vnútorné uzly predstavujú operácie (join, triedenie, spracovanie dát...) Listy predstavujú jednotlivé relácie s ktorými pracujeme 10 / 20
Strom plánu - príklad Strom plánu je možné zobraziť (v textovej podobe) pomocou EXPLAIN 11 / 20
Ohodnotenie plánu Čo to znamená vybrať najlepší plán? Planner vyberá plán podľa jeho odhadovanej ceny Na čom je táto cena založená? Prístupy na disk (I/O) - kľúčový faktor CPU čas potrebný na vykonanie Ide vždy o (viac či menej presný) odhad Odhad veľkosti medzivýsledkov a selektivity Založený na tabuľkových metadátach (štatistiky) Zlý odhad ceny => Výber nevhodného plánu => Neefektívne vyhodnotenie dotazu 12 / 20
Štatistiky Metadáta pre jednotlivé tabuľky a stĺpce, informácie o: Počte záznamov, počte obsadených stránok na disku (katalóg pg_class) Veľkosť záznamu, najčastejšie hodnoty, hranice histogramu, počet rôznych hodnôt, NULL hodnoty...(katalógy pg_statistics a pg_stats) Generovanie pomocou príkazu ANALYZE od 8.1 možnosť využiť AUTOVACUUM, v 8.3 vylepšené Nepresné, pri veľkých tabuľkách len na základe vybraných riadkov Možnosť zadať počet najčastejších hodnôt a počet intervalov histogramu pre každý stĺpec Lokálne pre každý stĺpec: ALTER TABLE SET STATISTICS Globálne: parameter default_statistics_target (default je 10) Aktuálne štatistiky sú kľúčové pre efektívne plánovanie 13 / 20
Generovanie plánov Čo znamená vygenerovať všetky vhodné plány? Cena uzlu v strome plánu závisí na selektivite (a teda štruktúre) jeho poduzlov Usporiadanie operácií v pláne určuje výslednú cenu Jazyk SQL nehovorí ako dotaz vykonať ale čo má byť výsledkom Preto musíme brať do úvahy všetky plány ktorých výsledkom sú požadované dáta Pre komplikované dotazy to ale nie je vždy možné 14 / 20
Vhodné plány Ako generovať vhodné plány? Ekvivalentné úpravy dotazu Preusporiadanie joinov tabuliek (komutatívne a asociatívne) Transformácia poddotazov do joinu Zmena poradia vyhodnotenia predikátov Rôzne spôsoby vykonania tej istej operácie Tri hlavné spôsoby zjednocovania tabuliek - nested loop, merge join, hash join Rôzne agregačné algoritmy (hashovanie, triedenie) Dva spôsoby prechádzania dát (sekvenčný prechod, prechod pomocou indexu) Cieľom je minimalizovať veľkosť medzivýsledkov Maximálna selektivita v dolných častiach stromu je rozhodujúca 15 / 20
Optimalizácia joinov Usporiadanie joinov je kľúčové pre kvalitu plánu Pre menší počet relácií sa dajú skúsiť všetky možnosti Optimalizačný algoritmus Založený na myšlienke dynamického programovania, vychádza z algoritmu pre System R (IBM, 70. roky) Prvý krok: Nájdi najlepší plán vyhodnotenia pre jednotlivé relácie Ďalšie kroky: Nájdi najlepší plán pre join výsledku predošlého kroku s reláciou ktorá v tomto výsledku zatial nie je (pre všetky výsledky) Ak výsledok obsahuje všetky relácie z dotazu máme optimálny plán Pre veľký počet relácií je to časovo neúnosné Optimizer používa genetický algoritmus pre nájdenie optimálneho plánu (GEQO) Beh algoritmu je po určitom čase ukončený a najlepší dosiahnutý plán je vyhlásený za optimálny 16 / 20
Outer joiny Inner joiny môžeme ľubovolne preusporiadať (komutativita, asociativita), outer joiny NIE Outer joiny zmenšujú počet možností pre usporiadanie joinov (v podstate je outer join ako jedna tabuľka) V niektorých prípadoch je možné transformovať ich na inner joiny Pri výbere usporiadania joinov je snaha zaradiť ich čo najneskôr aby sme zmenšili medzivýsledky 17 / 20
Poddotazy Cieľom planneru je eliminovať poddotazy presunom do hlavného dotazu vo forme zjednotenia Poddotaz v IN klauzuli Vždy dochádza ku eliminácii poddotazu Poddotaz vo FROM časti dotazu Eliminácia poddotazu je možná len pre jednoduché poddotazy (bez GROUP BY, HAVING, ORDER BY a agregačných funkcií) V prípade že eliminácia možná nie je, musí byť plánovanie poddotazu oddelené Poddotaz vo výraze Dochádza ku oddelenému vykonaniu poddotazu Ak neobsahuje premenné z hlavného dotazu, je vyhodnotený len raz Ak obsahuje premenné z hlavného dotazu, vyhodnocujeme ho opakovane 18 / 20
Executor Cieľom je vykonať operácie dané v strome plánu Záznamy sú načítané po jednom a na požiadanie Vnútorné uzly stromu Dostávajú záznamy od svojich poduzlov Spracujú tieto záznamy Vrátia výsledný záznam svojmu rodičovskému uzlu Typicky sú vnútornými uzlami operácie JOIN Listy stromu predstavujú načítanie dát z tabuliek Výsledné záznamy získame volaním operácie v koreni INSERT, UPDATE a DELETE v podstate ako SELECT INSERT - líši sa jedine tým, kam idú výsledky UPDATE / DELETE - Vracia naviac skrytý stĺpec s ID vybraných záznamov 19 / 20
Záver Ukázali sme si základy architerktúry a spracovania dotazov v Postgrese V každej oblasti je toho ale oveľa viac Ďalšie zdroje - dokumentácia, zdrojové kódy... Otázky? 20 / 20