On 9/1/25 12:42,
petrs...@gmail.com wrote:
> Ahojte,
>
> v pátek jsem narazil na něco, co mě překvapilo a potřeboval bych radu od
> zkušenějších. Máme takový lehce problematický kód, který občas
> výkonnostně zlobí. Vždycky jsem optimalizoval a od určité chvíle jsem už
> nevěděl, co a kde optimalizovat.
>
> No a v pátek mi to už nedalo a tak jsem se do toho ponořil a zjistil
> jsem, že problém je v tom, že samotný SELECT, který jsem si debugoval
> trvá sice 60 ms, ale když před ten SELECT hodím INSERT INTO, tak je tam
> úplně jiný exekuční plán a dotaz pak trvá 9.5 minuty. Výsledkem SELECTu
> je cca 4000 řádků, takže se nejedná o nijak velké množství.
>
> Původně jsem myslel, že je problém teda v cílové tabulce (indexy apod.).
> Ale opravdu je tam úplně jiný exekuční plán a výsledkem je, že nám to
> pak dobíhá výrazně později.
>
> Tady posílám oba dva exekuční plány:
>
> * pouze SELECT:
https://explain.dalibo.com/plan/d783640cac7ee837#
> * INSERT se SELECTem:
https://explain.dalibo.com/
> plan/22370ggc6008e44e#plan
Na 99% je to kombinace dvou faktorů
1) odhady jsou hodně nepřesné (v některých operacích o několik řádů)
2) INSERT nepoužívá paralelismus (není parallel-safe)
S nepřesnými odhady je plánování taková ruská ruleta, to může skončit
jakkoliv. S parallel query (pro čistý SELECT) se nejspíš vybere plán
který funguje celkem dobře, ale to je trochu náhoda. Ten INSERT
zablokuje parallel query, vybere se jiný plán, o trochu dražší, a ten
holt nefunguje tak dobře.
Jako experiment si můžete zkusit tohle (jednotlivě nebo dohromady):
1) vypnout paralelismus
SET max_parallel_workers_per_gather = 0;
2) vypnout nested loop joins
SET enable_nestloop = 0;
a uvidíte jestli ten SELECT bude stejně pomalý jako ten INSERT.
Jak to zrychlit ... těžká rada. Ideální by bylo zkusit zpřesnit ty
odhady, nějak. Ale ten dotaz není extra složitý, resp. ty podmínky
nejsou nějak komplikované.
Z toho co vidím je to spíš otázka toho že jsou tam složené podmínky. Ten
nejhorší odhad je pro join na podmínce
(ag.group_id = gb.group_id) AND (
a2.id = gb.advisor_id)
a to je problém pokud je to nějak korelované. DB to prostě odhadne pro
každou podmínku, a pak to vynásobí. Ale pokud je tam korelace tak to
může být podhodnocené - a pak je plán špatný.
Jako experiment bych zkusil rozdělit ten dotaz na dvě části:
1) materialized view / temporary tabulku pro tuhle část dotazu (až po
ten hashjoin)
2) posbírat statistiky
3) upravený dostaz s částí nahrazenou tím matview / temp tabulkou z (1)
A uvidíte.
T.