Čitanje plana
Plan je stablo operacija; koren vraća rezultat, a svaki čvor vuče redove od svoje dece. Sam EXPLAIN prikazuje procene planera (jedinice troška i očekivane redove); EXPLAIN ANALYZE zaista izvršava upit i dodaje stvarna vremena i brojeve redova, što plan čini čitljivim. BUFFERS dodaje koliko je stranica od 8 kB došlo iz keša (hit) ili sa diska (read). Uobičajen put kroz plan: nađite čvor sa najvećim ekskluzivnim vremenom, a zatim pogledajte zašto je tu: pogrešna procena redova iznad njega, nedostajući indeks ili podešavanje memorije.
Brojevi na čvoru
| Polje | Značenje |
|---|---|
| cost=0.29..8.31 | Procena planera u proizvoljnim jedinicama: trošak vraćanja prvog reda .. trošak vraćanja svih redova. Uporedivo samo unutar jednog plana. |
| rows= | Procenjeni redovi po petlji. Stvarna vrednost je takođe po petlji; pomnožite sa loops za ukupan broj. |
| actual time=0.05..12.3 | Milisekunde do prvog reda .. do poslednjeg reda, po petlji, uključujući decu. Alat oduzima decu da prikaže sopstveno vreme svakog čvora. |
| loops= | Koliko puta se čvor izvršio. Unutrašnja strana nested loop-a se izvršava jednom po spoljašnjem redu; ispod Gather-a jednaka je broju workera. |
| Buffers: shared hit=.. read=.. | Stranice pronađene u shared_buffers naspram pročitanih sa OS-a/diska. Čitanja su mesto gde odlazi I/O vreme; track_io_timing ga prikazuje kao I/O Timings. |
| Rows Removed by Filter | Redovi koje je čvor pročitao pa odbacio: najjasniji znak da nedostaje indeks. |
Pet uobičajenih problema
- Pogrešne procene. Ako čvor očekuje 10 redova, a dobije 100.000, planer je izabrao nested loop ili sort koji je imao smisla samo za 10. Pokrenite
ANALYZE; za predikate nad korelisanim kolonama napravite proširene statistike; za CTE i funkcije planer nema pojma i koristi podrazumevane pretpostavke. - Sequential scan sa selektivnim filterom. U redu je na malim tabelama. Na velikim je rešenje indeks nad filtriranim kolonama, idealno takav da odgovara i WHERE i ORDER BY.
- Spill. Sort external merge, Hash Batches > 1, HashAggregate na disku, lossy bitmape: operacija nije stala u
work_mem. Povećati ga za sesiju ili upit je jeftino; povećati ga globalno množi se brojem konekcija. - Nested loop nad skeniranjem. Unutrašnja strana se izvršava jednom po spoljašnjem redu; ako je to Seq Scan ili Materialize velikog skupa, vreme eksplodira. Unutrašnjoj strani treba indeks, ili planeru treba dozvoliti hash join.
- Index-only scan koji to nisu. Heap Fetches blizu broja redova znači da je visibility map zastarela: uradite VACUUM nad tabelom.
Dobijanje planova
-- PostgreSQL: najkorisniji oblik
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, FORMAT JSON) SELECT …;
-- za upise, unutar transakcije koju poništavate
BEGIN; EXPLAIN (ANALYZE, BUFFERS) UPDATE …; ROLLBACK;
-- automatsko logovanje sporih planova: auto_explain
LOAD 'auto_explain'; SET auto_explain.log_min_duration = '200ms'; SET auto_explain.log_analyze = on;
-- MySQL 8.0.18+
EXPLAIN ANALYZE SELECT …; -- stablo sa stvarnim vremenima
EXPLAIN FORMAT=JSON SELECT …; -- procene sa detaljima troška Pitanja
Zašto se vremena čvorova ne sabiraju u ukupno?
Stvarno vreme svakog čvora uključuje njegovu decu, pa alat prikazuje ekskluzivno vreme (čvor minus deca). Paralelni workeri rade istovremeno, pa se čvor ispod Gather-a sa loops=3 računa jednom, a ne tri puta; unutrašnje strane nested loop-ova se množe sa loops. Vreme planiranja i vreme trigera navode se odvojeno.
Kako izgleda dobar plan?
Onaj u kome su procene unutar faktora deset od stvarnih vrednosti, nijedan čvor ne ide na disk, najskuplji čvor je onaj koji očekujete (velika tabela koju zaista morate da pročitate), a baferi pročitani sa diska su mali u odnosu na hit-ove. Za takav plan lista nalaza je prazna.
Da li podržava particionisane tabele i paralelne planove?
Da: Append / Merge Append nad particijama, Gather i Gather Merge sa Workers Planned/Launched, Partial i Finalize agregate. Raščlanjivanja po workeru (VERBOSE) se parsiraju, ali se prikazuju kao deo čvora.
Mogu li da nalepim plan iz pgAdmin-a, DataGrip-a ili psql-a sa okvirima tabele?
Da. Redovi se čiste od psql znakova za okvir, zaglavlja QUERY PLAN i podnožja „(n rows)“. JSON kopiran iz GUI-ja kao jedan string pod navodnicima treba prvo osloboditi navodnika.
Kakva je podrška za MySQL u poređenju?
EXPLAIN ANALYZE stablo MySQL-a 8 ima isti oblik kao PostgreSQL tekstualni plan i potpuno je podržano; EXPLAIN FORMAT=JSON (samo procene) se mapira na isto stablo: tipovi pristupa postaju vrste skeniranja, nested_loop postaje join čvor, a filesort / temporary table oznake postaju nalazi. MariaDB ANALYZE FORMAT=JSON još nije podržan.