Hlavní navigace

Názory k článku PostgreSQL 12 – bude rok 2019 rokem Postgresu?

Článek je starý, nové názory již nelze přidávat.

  • 18. 6. 2019 2:12

    klokan

    Někde jsem četl, že kromě běžného JIT překladu také chtějí využívat GPU?

    A obecně si říkám, jestli by pro databázi mělo v principu smysl používat SIMD instrukce jako AVX: možná naivně si představuji, že u operací typu SELECT A WHERE A<B by mohly byt dost efektivní?

  • 18. 6. 2019 5:05

    Pavel Stěhule

    Využití GPU pro databáze je spíš pořád výzkum a ještě to bude hodně dlouho mimo upstream - u klasické relační databáze (řádkové) s klasickým executorem to zatím moc smysl nedává, i když lze GPU akcelerovat JOIN a ORDER BY - http://heterodb.github.io/pg-strom/release_note/ . Ve chvíli, kdy Postgres bude mít také sloupcově uložená data a push executor (rozšíření), tak by to smysl mělo. Předpoklad je i podpora in-memory tabulek, tak aby se nepálil čas načítáním dat z disku a rozbalováním z diskového formátu. Za pár let by to ale mohlo být.

  • 18. 6. 2019 22:36

    bez přezdívky

    Dobry den
    Predem dekuji za vyčerpavajici članek.
    Zaujala mě ta in-memory:
    Chtěl bych se zeptat když už jste to nakousl jak to s tou in-memory vypada a jestli se to bude cca za rok v nějake devel verzi testnout? Použivam u konkurence (funguje to super , ale neni to uplne levne )
    A potom jestli postgresSQL má nějaké možnosti řízeně držet v paměti tabulky popř indexy, nebo jejich části ?

  • 19. 6. 2019 5:46

    Pavel Stěhule

    Tak teoreticky si můžete in-memory vyzkoušet už nyní - skrze https://pgxn.org/dist/imcs/. Dále existují FDW drivery pro Redis https://github.com/nahanni/rw_redis_fdw, existuje driver pro Elastic. Možná za rok - za dva bude extenze založená na novém api pro custom heaps - integrace inmemory tabulek, pokud bude, bude asi následovat až za podporou column store a push (nebo block) executoru - což může být docela dlouhá cesta - jsou to změny napříč celou databází. Slyšel jsem o experimentech s persistentní RAM https://www.pgcon.org/2018/schedule/events/1154.en.html - celé je to běh na dlouhou trať - spíš tak 3-5 let.

    V Postgresu není žádná možnost jak zafixovat objekty v paměti - implementačně by to nebyl asi velký problém, ale nikdo ještě nevymyslel jak vyřešit optimalizaci přidělení do paměti - mohlo by se Vám stát, že vám jakýkoliv dotaz utaví IO, protože nebude mít k dispozici RAM, protože ta bude obsazená zafixovanými objekty, může se stát, že by Vám došla RAM pro zafixované objekty, atd - a do nějakých příliš jednoduchých řešení typu MySQL inmemory tables se nikomu moc nechce. Máte možnost rychle dostat objekty do RAM extenzí pg_prewarn - a pak, když budete mít dost RAM, tak se Vám tam udrží. V případě e-shopů se doporučuje mít velikost RAM 2-3 násobek velikosti db.

  • 18. 6. 2019 18:36

    Pavel Stěhule

    Těch důvodů může být víc - nicméně ty základní: 1) nepoužívá clusterované tabulky podle primárního klíče a zrovna tak operace nad primárním klíčem nejsou nějak speciálně optimalizované 2) postgres používá jeden proces na jedno spojení - to je vůči jednoduchým dotazům příliš drahé, 3) není tam sdílená plan cache, která pro jednoduché dotazy může zafungovat, 4) co vím, tak nikdo z vývojářů to pro takový case netestuje a neoptimalizuje.

    To co je cenné na Postgresu jsou chytré datové typy, bohatá nabídka funkcí a indexů, hodně dobrý optimalizátor, který má co optimalizovat - interně je tam podpora několika různých algoritmů pro join, podpora paralelizace dotazů, atd. Pokud nic z toho nepoužijete, tak je otázkou proč Postgres. Hodně samozřejmě záleží na zátěži. To, že v předchozím odstavci tyto operace nejsou extra optimalizované si všimnete až při zátěži blížící se desítce tisíc transakcí za sec a výše. Pokud máte menší zátěž, tak je to jedno.

  • 18. 6. 2019 16:13

    ITFUN

    V prvom rade vďaka za článok - je veľmi odborne napísaný. Ja sám však som laik, ktorý by rád použil PostgreSQL práve v kombinácii s Javou. Na youtube som našiel toto video https://www.youtube.com/watch?v=-tUdTxHOB90

    Toto video porovnáva out-of-the-box výkon PostgreSQL 9.2, Oracle 11g Xe a Microsoft SQL 2005 EXPRESS.
    Videom som bol troška zdesený, že v rýchlosti vyhrával práve MS SQL v Express verzii.
    Veľmi by ma potešil /možno aj ostatných/ článok o PostgreSQL optimalizácii. Jediné čo viem o optimalizácii ohľadne databázy, je nutnosť dostatočnej veľkosti RAM-ky a použitie rýchleho SSD disku najmä čo sa týka výkonu 4K Write a 4K Read - súdiac podľa článku na phoronix.com https://www.phoronix.com/scan.php?page=article&item=intel-ssd-660p&num=2 V článku na phoronix.com je test zvaný PostgreSQL pgbench v10.3 a v tomto teste Intel Optane 900p má najvyšší výkon v teste - pretože má ohromný výkon v 4k Read a v 4k Write.

    V postgresql plánujem vytvoriť 600 tabuliek, ktoré budú mať tieto stĺpce.
    1.stĺpec - BIGSERIAL -AUTOINCREMENT Primary key napríklad 1,2,3,4,5,6 atď.
    2.stĺpec- TIMESTAMP napríklad 2019-01-29 01:36:14.123456 - bez časovej zóny
    3.stĺpec REAL
    4.stĺpec REAL
    5.stĺpec REAL
    6.stĺpec REAL
    V SQL dotazoch chcem použiť zoradenie údajov podľa TIMESTAMP - údaje budú tiecť z postgresql do mojej aplikácii napísanej v Jave.
    Úplne jednoduché SELECTY a INSERTY.
    Ak by niekto vedel poradiť ako mám zoptimalizovať PostgreSQL v takomto mojom scenári - poprosím o radu.

  • 18. 6. 2019 18:59

    Pavel Stěhule

    Ten test je zvláštní - jestli jsem to pochopil, tak je to o rychlostech INSERTu (z toho videa ale nejde pořádně nejde odhadnout, co to dělá). MS SQL i v Express edici je psaný primárně pro Windows (vlastně pouze pro windows). Oracle i Postgres jsou naopak primárně Unix databáze - vnitřek Oracle neznám, ale o Postgresu se dá říct, že je to primárně Unix databáze, kterou lze spustit na windows, která nevyužívá žádné speciální optimalizace pro win. U téhle úlohy je to primárně o tom, kolik Vám dá operační systém IO. Postgres používá filesystém API bez jakékoliv prioritizace - a koneckonců dá se předpokládat, že Microsoft bude mít svou databázi pro svůj operační systém vyladěnou (a naopak). Z toho videa se vůbec nedá říct, v čem je problém - není vidět zátěž CPU, IO a já vůbec neznám .NET driver, abych odhadnul v čem je problém.

    Na tom Vašem příkladu v podstatě není co optimalizovat - je to vlastně jen asi o konfiguraci shared buffers, aby se vám v RAMce udržely důležité části indexů. Případně možná o vypnutí synchronního commitu. Jinak ty phorox testy jsou pro praktický život dost na nic. Určitě nikdy nebudete mít a ani nechcete mít stejnou zátěž jako generuje pgbench. Je tam hromada dalších faktorů, které mohou mít vliv, a syntetické testy nic neříkají o normální zátěži, stejně tak o zátěži, kterou může generovat vaše aplikace.

  • 18. 6. 2019 22:16

    ITFUN

    Vážený pán Stěhule,
    veľmi pekne ďakujem za vašu reakciu a za vaše cenné pripomienky. Váš príspevok som si uložil, budem ho mať na očiach, keď budem ladiť svoju postgresql databázu, ktorá bude prevádzkovaná samozrejme na Linuxe.
    Ohľadne toho videa určite máte pravdu, MS SQL je vyladený pre Windows a Oracle a Postgresql sú pre Unix/Linux.
    Ešte raz ďakujem za vaše cenného pripomienky.

  • 19. 6. 2019 10:53

    thr

    To tvrzení o MS SQL bych si dovolil rozporovat, Microsoft tu Linux verzi vyvíjí už déle a od prvních kroků, kdy to bylo na Linux spíš jen "nabastleno" se to docela posunulo a optimalizace už je dnes slušná (zlí jazykové dokonce tvrdí, že občas podává MS SQL na Linuxu lepší výsledky než na Windows Serveru). To vámi odkázané video nemá se současným stavem nic společného, je z roku 2013, kdy Linux verze MSSQL ještě neexistovala a mimochodem v testu užitý SQL Express 2005 byl dost přežitý už tehdy i na Windows.

    Jinak ale smysl Linux MS SQL verze je spíš k možnosti nasazení aplikace vzniklé na Windows a MS SQL na více platforem. Pokud tvoříte něco od nuly a navíc jste si opravdu jistý s tím, že to bude běhat na Linuxu, tak pak skutečně nemá pořizování licence MS SQL smysl (zadarmo je jen Express, který má limitů spoustu). Nicméně výkonu a stability bych se nebál...

  • 19. 6. 2019 10:29

    Youda

    Jenom podotek, doporucuju se podivat na TimescaleDB, je zalozena na Postgresu a slouzi prave pro ukladani time-series dat.

    https://github.com/timescale/timescaledb

    Alternativa je InfluxDB

    Jinak pokud chces tyto data mit v cistem Postgresu, rozhodne pouzij partitioning podle timestampu, ten v danem pripade hodne pomuze.

  • 19. 6. 2019 18:26

    bez přezdívky

    K tomuhle bych měl dotaz zase já. Timescaledb je teď všude plno, ale nikdo nezmiňuje Citus. Přitom mi to přijde také jako zajímavá alternativa.

  • 19. 6. 2019 19:27

    rpajik

    Influxdb je hodně lehký, ale ve chvíli kdy tam máte několik TB dat, tak to neni žádná hitparáda. Ve free verzi neškálovatelné. Věci jako downsampling nad takhle velkou databází jsou nepoužitelné.

  • 19. 6. 2019 21:18

    ebik

    Navíc má Influxdb parser vstupu (line formát) který není konzistentní sám se sebou a vhodným zřetězením backslashů vám na insert vrátí 500 Internal server error. Takže nesmíte dát útočníkovi plnou kontrolu nad jmény tagů , metrik a nad retezcovými hodnotami (stačí zakázat uvozovku uvnitř řetězce a backslash a navíc whitespace, čárky a rovnítka ve jménech metrik a tagů - ty backslahe ale můžou být problém u Windows style paths - mělo by ale stačit zakázat koncový backslash který by bylo možné interpretovat jako escape: tedy na konci, před whitespace, čárkou a rovnítkem). Pokud bych parser bral jako ukázku kódu jakým je influx napsaný, tak bych mu data nesvěřil.

  • 19. 6. 2019 9:17

    bez přezdívky

    Zdravim chtěl bych se zeptat zda nahodou někdo nemate zkušenost s range typy.
    Jde mi konkretne o case kdy mám v tabulce sloupecky date_from, date_to a přemýšlím zda ma smysl modelovat to jako tsrange.
    Řeším otázky jako
    a) jak efektivní je uložení skrz zabrané místo na disku
    b) jak efektivní je přístup k pouze jedné z hodnot
    c) jak efektivní je řazení například podle date_from
    d) jak efektivní je indexování GIST na tsrange vs btree na samostatných sloupcích
    e) libovolné prakticke zkušenosti

    snažil jsem se googlit hledat benchmarky ale příjde mi že na to jak je toto potenciálně zajimavý typ tak je k němu až převapivě málo informací

    Děkuji

  • 19. 6. 2019 19:42

    Pavel Stěhule

    range typy jsem nepoužil - nicméně na některé otázky znám odpověď. timestamp zabírá 8 bajtů (na 64bit). tsrange je tzv varlena typ, takže může zabírat od 4 bajtů do 1GB. Co jsem se díval, tak zabíral 25 bajtů. Tabulka o 2 timestampech s 1M řádků má 42MB, s tsrange 50MB. Index nad timestampem má 21MB, nad tsrange 39MB.

    range type se používal a používá jednak pro zajištění nepřekrývání intervalů, druhak pro dotazy, kde je podmínka typu date_from < x and x < date_to (jestli si to pamatuji správně). Na takových dotazech jsou klasické indexy slabé - před range typy se tyto dotazy převáděly do 2D prostoru a používaly se prostorové indexy. S range typy takové harakiri se dělat nemusí. Před pár lety jsem ovšem dělal nějaké testy a i bez range typů to nějak fungovalo díky bitmap heap scanu - range type by ovšem měly být výrazně lepší.

    GiST index je obecnější forma Btree. Chování těchto dvou indexů by mělo být hodně podobné. Nicméně u range (X1, X2) se využívá pro optimalizaci faktu, že X1 je vždy <= X2. To, když použijete 2x timestamp neplatí a vyhledávání, výběr nemůže být tak efektivní.

  • 19. 6. 2019 14:53

    ITFUN

    To thr
    Ja som dlho fungoval na platforme Windows a MS SQL Express a bol som celkom spokojný. Potom to prišlo a namiesto niekoľko desatok tabuliek som potreboval niekoľko stoviek tabuliek, tak som sa začal obzerať po PostgreSQL, ktorý nemá také limity ako MS SQL Express. Navyše moje programy napísané ešte v staručkom VISUAL BASICU som potreboval modernizovať a vybral som si Javu, v ktorej to znovu naprogramujem a úplne prirodzene som skončil u platformy Linux/GNU.

    To Youda
    Veľmi pekne ďakujem za tvoje usmernenie ohľadne databázy pre timeseries a ohľadne partitioningu. Tvoj príspevok som si tiež uložil a budem skúmať možnosti optimalizácie postgresql.

  • 19. 6. 2019 16:19

    Youda

    Ahoj,

    zalezi, kolik chces do te Javy dat sadla, pokud ale nejsi liny se ucit, zkus to stavet nad Spring Boot a buildovat mavenem.
    Ma to pekne feautury, ma v sobe zabudovany Hibernate, ktery ja osobne pouzivam jako mapovac resultsetu na datove beany, SQL pouzivam nativni od Potgresu.
    Dalsi featury pridas pres maven. To je nastroj, kteremu reknes, ze chces mit podporu SNMP a on vsechny potrebne knihovny stahne z i ternetu a prida ti je do projektu, nakonez i do vysledneho JAR baliku.

    https://www.baeldung.com/spring-boot-console-app

    Pro vyvoj pozuivam STS (spring tool suite) postaveny na Eclipse.

    Kdyz se v tom naucis delat, zpatky uz chtit nebudes, proti hole jawe je to jak nebe a dudy.

  • 19. 6. 2019 20:51

    lzap

    Opravdu volba log_transacti­on_sample_rate = 0.01 zaloguje "každou stou transakci"? Svým názvem se jedná spíše o pravděpodobnost vyjmutí vzorku, tzn. s pravděpodobností 1% se náhodně vybere nějaký případ. Mělo by to být zhruba v jednom případu ze sta, nikoli ale každá stá transakce.

    Skvělý článek, díky za něj.

  • 20. 6. 2019 6:17

    Pavel Stěhule

    Máte pravdu, napsal jsem to špatně - lepší by bylo, kdybych napsal "cca každou stou transakci". Je to implementované pomocí náhodných čísel.

  • 20. 6. 2019 8:44

    lzap

    Díky za upřesnění, to mě právě překvapilo. Něco podobného jsem nedávno implementoval, je to dobrý způsob když je logů moc.

    Snažíme se u našeho velkého Ruby on Rails projektu zrušit podporu MySQL a Sqlite3 a plně se soustředit už jen na PostgreSQL s využitím různých optimalizačních vychytávek.

  • 20. 6. 2019 8:59

    Ivan Brezina

    Trochu me zarazi to klicove slovo MATERIALIZED. Oracle si tohle rozhoduje "za jizdy", kdyz generuje exekucni plan. A pokud to nefunguje na 100% tak je mozne to ohnout pomoci hintu /*+ INLINE */ vs. /*+ MATERIALIZED */. Prekvapuje me, ze si PostgreSQL "zasvini" gramatiku SQL pridanim klicoveho slova, ktere treba za par let ani nebude potreba.

    Takhle se spis chova MySQL kde se rozsiruje gramatika kvuli kdejake kravine.

  • 20. 6. 2019 11:16

    Pavel Stěhule

    MATERIALIZED už v Postgresu bylo (MATERIALIZED VIEW) a i když je to klíčové slovo, tak není rezervované - takže to neblokuje použití "materialized" jako SQL identifikátoru. Hinty v Postgresu nejsou - a pro tento konkrétní případ se zavádět nebudou. Na druhou stranu CTE v tom historickém chování se používalo jako hint (vynucovalo si materializaci), takže řešilo nějakou situaci, kterou optimalizátor nedokázal pořešit (např. kvůli špatným odhadům). A proto bylo potřeba rozšířit gramatiku (nicméně žádné klíčové slovo se v tomto případě nepřidávalo)

  • 26. 6. 2019 13:20

    bez přezdívky

    Dobrý deň.

    Chcel by som k CTE a ich fungovaniu / zmenám pridať aj svoj pohľad / skúsenosť.

    Používame (s kolegami) ich často, aj pre bežné nerekurzívne dotazy. Sú pre nás intuitívnejšie na členenie / pochopenie / udržovanie kódu, aj keď to je do istej miery vec osobnej preferencie a zvyku.

    Samozrejme máte pravdu v tom, že keď niekde spôsobujú väčšie než zanedbateľné spomalenie, tak to píšeme / členíme podľa potreby aj iným spôsobom.

    Avšak to, že sa CTE materializuje, sa mi zdá intuitívne správne, práve to by som čakal.

    Nie je to predsa "common view expression" alebo "common subquery definition for reuse".

    A nie je to čiste kozmetická vec týkajúca sa len preferencií členenia kódu:

    1) Keď sa vo vnútri CTE používajú funkcie / výrazy, ktoré pri viacnásobnom volaní nevracajú rovnaký výsledok.

    Je to rovnaký príncíp, ktorý ste uviedli ako dôvod v kapitole "Generované sloupce" -

    "Aby byla zajištěna konzistence mezi uloženými a virtuálními počítanými sloupci, tak (podle ANSI) jsou umožněny pouze výrazy, které jsou (terminologií Postgresu) neměnné (immutable)".

    T.j. ak v CTE použijem napr. random(), pri viacnásobnom inlinovaní tohto CTE nebude jeho obsah rovnaký.

    2) Sú prípady použitia, keď pri definovaní jednotlivých CTE v rámci celého bloku / sekvencie CTE (tvoriacich jeden dotaz) je dôležité aj poradie ich definície.

    Toto sa inlinovaním poruší / nedodrží. V rýchlosti ma napadajú napr. časové stopy (clock_timestampt() ), ale určite budú existovať aj iné prípady.

    To súčasné kritérium, ktoré uvádzate, "jedno použitie - inlinuj, viac použití - materializuj", by zatiaľ stačilo pre "ošetrenie" prípadu 1) , ale už nie prípadu 2) .

    Neviem, či sa do budúcna toto kritérium bude upravovať. Ale očakával by som, že zrýchlenie / optimalizácia nebude jediným, ani prvoradým kritériom.

    Z môjho pohľadu to najprv má fungovať správne (myslené ako doterajší / existujúci kód pod novou verziou), potom až sledujem rýchlosť.

    26. 6. 2019, 13:20 editováno autorem komentáře

  • 26. 6. 2019 22:16

    Pavel Stěhule

    Možná jste přehlédl, že jsem v textu psal, že pokud je relace referencovaná více krát, tak automaticky dojde k materializaci. Tudíž problémy s random() nebo clock_timestamp nemohou nastat. Automatický inline je pouze tehdy, když je relace použitá pouze jednou.

    ANSI SQL vůbec nic neříká o optimalizaci a inlining jednou použité reference mně osobně dává větší smysl než materializace. Vývojáři této funkce spíš z jednoduchosti a konzistence implementace inlining vůbec nebrali v potaz - a časem se používání nerekurzivního CTE v Postgresu stalo určitým typem hintu - v podstatě jediným jednoduchým způsobem, jak si vynutit materializaci, a případně jak zahodit odhady, pokud byly chybné. Inlining se v tomto kontextu vůbec neuvažoval - v případě, že by chtěl někdo inlining, tak prostě napsal derivovaný poddotaz.

    Všechny jiné databáze ovšem zvolily jinou strategii - a jelikož se v posledních 5 letech intenzivně začalo migrovat z těchto databází do Postgresu, tak se začalo ukazovat, že ta jednoduchá implementace "materializace vždy" je příliš jednoduchá - a nedává smysl (pro pouze jednou referencované CTE). To nikdo nerozporoval - problém byl jen, jak minimalizovat impakt na stávající uživatele - jak moc porušit svá pravidla - defacto se jedná o hint. Drtivé většině uživatelů by inline měl spíše pomoc, ale určitě budou existovat uživatelé, kteří CTE použili kvůli zahození odhadů, a těm se výpočet může zhoršit.

    Nevím o tom, že by ve standardu bylo něco o garanci pořadí provedení CTE. To i při materializaci pokud tam nemáte závislosti, tak se teoreticky může vykonávat v náhodném pořadí - teoreticky i např. v jiném procesu, kdy se bude synchronizovat pouze výsledek (což zatím není implementováno, ale bere se to jako možnost).

    Porušení zpětné kompatibility je vždy problém, a vývojáři to neberou na lehkou váhu. Vždy někoho naštvete - noví uživatelé preferují co nejmodernější implementaci (optimalizaci) pokud možno kompatibilní se světem. Stávající uživatelé preferují kompatibilitu. V tomto případě by asi nejčastějším nepříjemným efektem mohlo být zpomalení některých dotazů (ale to je v každé verzi, že cca 2-5% dotazů je v nové verzi pomalejší v důsledku jiné optimalizace, jinak počítaných statistik, odhadů). Počítat jinak by to nemělo, pokud ano, tak bych to spíš než na bugu viděl jako zákaznický kód, který se pohybuje v zóně nejednoznačně definovaného chování, který v nějaké verzi fungovat může, ale v další nemusí (v každé verzi Postgresu se hodně mění optimalizátor, executor, ..)

  • 27. 6. 2019 11:49

    bez přezdívky

    Neprehliadol, ale skúsim vysvetliť lepšie, ako som to myslel.
    Rozdiely, ktoré si myslím, že môže spôsobiť inlinovanie CTE oproti materializácii som vysvetlil v bodoch 1) a 2) . Tiež som uviedol (tu súhlasím s Vami) že to pravidlo "jedno použitie - inlinuj, viac použití - materializuj" stačí na to, aby sa problém 1) nevyskytol. Ale to pravidlo ("jedno použitie - inlinuj, viac použití - materializuj") nestačí, aby sa ošetril alebo zamedzil výskyt problému podľa 2) , t.j. poradie, v akom sa vyhodnotia (napr. ten clock_timestamp() ). Príklad - skúšal som ho vo verzii 11, tak ako chápem a predpokladám, že to bude fungovať vo verzii 12 (bohužiaľ neviem, ako to lepšie naformátovať v komentári):

    with tmp_1 as
    (
    select clock_timestamp() as cas_zaciatku
    ),
    tmp_2 as
    (
    /* simulacia casovo narocnej casti (v reale tu moze byt viacero CTE, nez sa dopracujem k finalnemu dotazu a casu), parameter v generate_series sa da upravit pre viditelnejsie casove rozdiely */
    select *,
    row_number() over(order by 0 = 1) as nejaka_podmienka
    from generate_series(1, 10000000) as gs
    )
    select
    t1.cas_zaciatku as cas_zaciatku_ma­terialise,
    clock_timestamp() as cas_zaciatku_inline
    from tmp_1 t1
    join tmp_2 t2 on
    t2.nejaka_podmienka = 1
    ;

    Rozumiem, a nerozporujem, že s novými verziami sa rýchlosť / plánovanie / optimalizácia mení. Len v tomto prípade chcem poukázať na to, že doterajší kód už môže fungovať nielen pomalšie, ale aj s inou logikou.
    Ak pre uvedený príklad (hore) s clock_timestamp() doteraz vždy bol naplnený čas začiatku spracovania, tak zaručoval aj poradie (naplnených dát) podľa tohto času začiatku. Po novom už to neplatí - zoradené to bude podľa toho, čo skôr dokončí spracovanie (t.j. kde sa inlinované CTE vo výslednom dotaze dostane do spracovania skôr).

  • 27. 6. 2019 13:48

    Pavel Stěhule

    Ten Váš příklad dává na 11 i na 12ce téměř stejné výsledky

    -- 12
    ┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐
    │                                             QUERY PLAN                                              │
    ╞═════════════════════════════════════════════════════════════════════════════════════════════════════╡
    │ Nested Loop  (cost=0.02..375625.04 rows=50000 width=16)                                             │
    │   CTE tmp_1                                                                                         │
    │     ->  Result  (cost=0.00..0.01 rows=1 width=8)                                                    │
    │   ->  CTE Scan on tmp_1 t1  (cost=0.00..0.02 rows=1 width=8)                                        │
    │   ->  Subquery Scan on t2  (cost=0.00..375000.00 rows=50000 width=0)                                │
    │         Filter: (t2.nejaka_podmienka = 1)                                                           │
    │         ->  WindowAgg  (cost=0.00..250000.00 rows=10000000 width=13)                                │
    │               ->  Function Scan on generate_series gs  (cost=0.00..100000.00 rows=10000000 width=1) │
    └─────────────────────────────────────────────────────────────────────────────────────────────────────┘
    (8 rows)
    
    -11
    ┌─────────────────────────────────────────────────────────────────────────────────────────┐
    │                                       QUERY PLAN                                        │
    ╞═════════════════════════════════════════════════════════════════════════════════════════╡
    │ Nested Loop  (cost=25.01..47.60 rows=5 width=16)                                        │
    │   CTE tmp_1                                                                             │
    │     ->  Result  (cost=0.00..0.01 rows=1 width=8)                                        │
    │   CTE tmp_2                                                                             │
    │     ->  WindowAgg  (cost=0.00..25.00 rows=1000 width=13)                                │
    │           ->  Function Scan on generate_series gs  (cost=0.00..10.00 rows=1000 width=5) │
    │   ->  CTE Scan on tmp_1 t1  (cost=0.00..0.02 rows=1 width=8)                            │
    │   ->  CTE Scan on tmp_2 t2  (cost=0.00..22.50 rows=5 width=0)                           │
    │         Filter: (nejaka_podmienka = 1)                                                  │
    └─────────────────────────────────────────────────────────────────────────────────────────┘
    (9 řádek)
  • 27. 6. 2019 14:00

    Pavel Stěhule

    Implementované je to jinak, ale chování je stejné (v tomto případě) Jakýkoliv dotaz, který by byl závislý na pořadí provádění je koledování si o průser.

  • 2. 7. 2019 10:26

    bez přezdívky

    Snažím sa pochopiť Vaše komentáre, zatiaľ bez úspechu. Uviedol som príklad, na ktorom ukazujem, že výsledky sú odlišné (aj keď boli len simulované vo verzii 11 tak, ako si predstavujem, že bude fungovať verzia 12, to uznávam a uviedol som).
    Vy ste uviedli, že sú to "téměř stejné výsledky". Veď "téměř stejné" znamená nie stejné, a teda odlišné, teda to, čo sa snažím ukázať.
    Ďalšia vec - hovoríte "výsledky", ale pripojili ste exekučné plány. O plánoch nehovorím, nerozumiem, s čím tu súvisia.
    S tým, že poradie nezávislých dotazov v rámci CTE nie je garantované, to je mi jasné, uznávam, že som z tohto pohľadu nezvolil správny príklad.

    Mám už teraz nainštalovanú betu verzie 12 (výpis z funkcie version: PostgreSQL 12beta2 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit) .
    Chcel som si priamo na nej otestovať a možno pripraviť príklad, ktorý by lepšie ilustroval, čo chcem povedať.
    Teraz mám ale problém nasimulovať funkčnosť CTE pre NOT MATERIALIZED variantu (inline).

    with tmp_1 as not materialized
    (
      select
        pg_sleep(3) as sleep,
        clock_timestamp() as cas
    )
    select * from tmp_1 union all
    select * from tmp_1
    ;

    Tu to vráti rovnaké časy, a dotaz beží 3 sekundy, z toho usudzujem, že sa CTE pustilo iba raz, a chová sa rovnako, ako keby bolo materializované.

    select * from
      (
        select
          pg_sleep(3) as sleep,
          clock_timestamp() as cas
      ) tmp_1
    union all
    select * from
      (
        select
          pg_sleep(3) as sleep,
          clock_timestamp() as cas
      ) tmp_1
    ;

    Keď obsah toho CTE inlinujem ručne, tak už to vráti rôzne časy, a beží to 6 sekúnd (t.j. oba 3-sekundové sleepy).
    Ako teda simulovať CTE, ktoré sa bude chovať ako naozaj inlinovaný kód ?

  • 4. 7. 2019 8:23

    Pavel Stěhule

    Ono se to dost špatně simuluje - vzhledem k tomu, že inlining je pouze v případě, že se referencuje pouze jednou.

    Pozná se to podle prováděcích plánů (že vám zmizí CTE):

    create table dual(a int);
    postgres=# explain analyze with tmp_1 as
    (
      select * from dual
    )
    select * from tmp_1;
    +----------------------------------------------------------------------------------------------+
    |                                          QUERY PLAN                                          |
    +----------------------------------------------------------------------------------------------+
    | Seq Scan on dual  (cost=0.00..1.01 rows=1 width=4) (actual time=0.020..0.023 rows=1 loops=1) |
    | Planning Time: 0.112 ms                                                                      |
    | Execution Time: 0.058 ms                                                                     |
    +----------------------------------------------------------------------------------------------+
    (3 rows)
    
    postgres=# explain analyze with tmp_1 as materialized
    (
      select * from dual
    )
    select * from tmp_1;
    +------------------------------------------------------------------------------------------------------+
    |                                              QUERY PLAN                                              |
    +------------------------------------------------------------------------------------------------------+
    | CTE Scan on tmp_1  (cost=1.01..1.03 rows=1 width=4) (actual time=0.029..0.032 rows=1 loops=1)        |
    |   CTE tmp_1                                                                                          |
    |     ->  Seq Scan on dual  (cost=0.00..1.01 rows=1 width=4) (actual time=0.023..0.025 rows=1 loops=1) |
    | Planning Time: 0.169 ms                                                                              |
    | Execution Time: 0.086 ms                                                                             |
    +------------------------------------------------------------------------------------------------------+
    (5 rows)
    
    
    
    -- fx je stable
    postgres=# explain analyze with tmp_1 as
    (
      select fx(a) from dual
    )
    select * from tmp_1;
    NOTICE:  spusteno fx(1)
    +----------------------------------------------------------------------------------------------+
    |                                          QUERY PLAN                                          |
    +----------------------------------------------------------------------------------------------+
    | Seq Scan on dual  (cost=0.00..1.26 rows=1 width=4) (actual time=0.129..0.132 rows=1 loops=1) |
    | Planning Time: 0.166 ms                                                                      |
    | Execution Time: 0.182 ms                                                                     |
    +----------------------------------------------------------------------------------------------+
    (3 rows)
    
    postgres=# explain analyze with tmp_1 as
    (
      select fx(a) from dual
    )
    select * from tmp_1 union all select * from tmp_1;
    NOTICE:  spusteno fx(1)
    +-------------------------------------------------------------------------------------------------------------+
    |                                                 QUERY PLAN                                                  |
    +-------------------------------------------------------------------------------------------------------------+
    | Append  (cost=1.26..1.31 rows=2 width=4) (actual time=0.138..0.146 rows=2 loops=1)                          |
    |   CTE tmp_1                                                                                                 |
    |     ->  Seq Scan on dual  (cost=0.00..1.26 rows=1 width=4) (actual time=0.127..0.131 rows=1 loops=1)        |
    |   ->  CTE Scan on tmp_1  (cost=0.00..0.02 rows=1 width=4) (actual time=0.136..0.140 rows=1 loops=1)         |
    |   ->  CTE Scan on tmp_1 tmp_1_1  (cost=0.00..0.02 rows=1 width=4) (actual time=0.001..0.002 rows=1 loops=1) |
    | Planning Time: 0.259 ms                                                                                     |
    | Execution Time: 0.227 ms                                                                                    |
    +-------------------------------------------------------------------------------------------------------------+
    (7 rows)
    
    postgres=# explain analyze with tmp_1 as not materialized
    (
      select fx(a) from dual
    )
    select * from tmp_1 union all select * from tmp_1;
    NOTICE:  spusteno fx(1)
    NOTICE:  spusteno fx(1)
    +-----------------------------------------------------------------------------------------------------------+
    |                                                QUERY PLAN                                                 |
    +-----------------------------------------------------------------------------------------------------------+
    | Append  (cost=0.00..2.53 rows=2 width=4) (actual time=0.128..0.177 rows=2 loops=1)                        |
    |   ->  Seq Scan on dual  (cost=0.00..1.26 rows=1 width=4) (actual time=0.126..0.130 rows=1 loops=1)        |
    |   ->  Seq Scan on dual dual_1  (cost=0.00..1.26 rows=1 width=4) (actual time=0.042..0.043 rows=1 loops=1) |
    | Planning Time: 0.276 ms                                                                                   |
    | Execution Time: 0.247 ms                                                                                  |
    +-----------------------------------------------------------------------------------------------------------+
    (5 rows)
    
    -- jakmile nastavim fx volatile
    postgres=# explain analyze with tmp_1 as
    (
      select fx(a) from dual
    )
    select * from tmp_1;
    NOTICE:  spusteno fx(1)
    +------------------------------------------------------------------------------------------------------+
    |                                              QUERY PLAN                                              |
    +------------------------------------------------------------------------------------------------------+
    | CTE Scan on tmp_1  (cost=1.26..1.28 rows=1 width=4) (actual time=0.249..0.254 rows=1 loops=1)        |
    |   CTE tmp_1                                                                                          |
    |     ->  Seq Scan on dual  (cost=0.00..1.26 rows=1 width=4) (actual time=0.239..0.244 rows=1 loops=1) |
    | Planning Time: 0.155 ms                                                                              |
    | Execution Time: 0.352 ms                                                                             |
    +------------------------------------------------------------------------------------------------------+
    (5 rows)

    Jinak v dokumentaci pisi

    + <literal>NOT MATERIALIZED</li­teral> is ignored if it is attached to
    + a <literal>WITH</li­teral> query that is recursive or is not
    + side-effect-free (i.e., is not a plain <literal>SELEC­T</literal>
    + containing no volatile functions).

    coz znamena, ze ve vsech funkcich, ktere jste zkousel doslo k materializaci, bo nejsou non volatile. Tudiz by to melo byt i relativne bezpecne - vuci volatile funkcim se to za vsech okolnosti chova postaru.

  • 16. 7. 2019 17:26

    bez přezdívky

    Ďakujem za článok, vyhľadávam ho každý rok, takže sa určite teším na článok o PG13.Z tohto ročných novidiem ma zaujali nedeterministicke COLLATE pre ICU.

    Je možné použiť kombináciu, ktorá by našla všetky case insensitive vrátane odstránenia diakritiky?

    SELECT * FROM foo WHERE b like '%zluty%';
    ┌───────────┬───────────┐
    │     a     │     b     │
    ╞═══════════╪═══════════╡
    │ Žlutý kůň │ Žlutý kůň │
    └───────────┴───────────┘
    (1 row)

    16. 7. 2019, 17:27 editováno autorem komentáře

  • 16. 7. 2019 20:48

    Pavel Stěhule

    Myslim si, ze ano. Jde jen o to si zkombinovat vlastni locale.