Për çfarë nuk flet EXPLAIN, dhe si ta bëjmë atë të flasë

NjĂ« pyetje klasike me tĂ« cilĂ«n zhvilluesi i drejtohet DBA-sĂ« sĂ« tij ose pronarit tĂ« biznesit — konsulenti pĂ«r PostgreSQL, pothuajse gjithmonĂ« tingĂ«llon e njĂ«jtĂ«: «Pse kĂ«rkesat ekzekutohen kaq ngadalĂ« nĂ« bazĂ«n e tĂ« dhĂ«nave?»

Një grup tradicional shkaksh faktorësh:

  • algoritmi joefikas
    kur vendosni të bëni JOIN disa CTE me disa dhjetëra mijëra regjistra
  • statistika joaktuale
    nëse shpërndarja faktike e të dhënave në tabelë ndjeshëm ndryshon nga ajo e mbledhur nga ANALYZE herën e fundit
  • "bllokim" nĂ« burime
    dhe tashmë nuk ka mjaft kapacitete kompjuterike CPU, duke e çuar në një rritje të vazhdueshme të memorie ose disku që nuk arrin të përmbushë të gjitha "kërkesat" e DB-së
  • e bllokimeve nga proceset konkurrente

Dhe nëse bllokimet merren dhe analizan relativisht me vështirësi, për gjithë të tjerat na mjafton plani i kërkesës, i cili mund të merret me anë të kryerjes EXPLAIN (është më mirë, sigurisht, direkt EXPLAIN (ANALYZE, BUFFERS) 
) ose moduli auto_explain.

Por, siç thuhet në të njëjtën dokumentacion,

«TĂ« kuptosh planin Ă«shtĂ« njĂ« art, dhe pĂ«r ta zotĂ«ruar atĂ«, nevojitet njĂ« pĂ«rvojĂ« e caktuar,  »

Por mund të kalosh pa të, nëse përdor një mjet të duhur!

Si duket zakonisht plani i kërkesës? Ashtu si kjo:

Index Scan using pg_class_relname_nsp_index on pg_class (actual time=0.049..0.050 rows=1 loops=1)
  Index Cond: (relname = $1)
  Filter: (oid = $0)
  Buffers: shared hit=4
  InitPlan 1 (returns $0,$1)
    ->  Limit (actual time=0.019..0.020 rows=1 loops=1)
          Buffers: shared hit=1
          ->  Seq Scan on pg_class pg_class_1 (actual time=0.015..0.015 rows=1 loops=1)
                Filter: (relkind = 'r'::"char")
                Rows Removed by Filter: 5
                Buffers: shared hit=1

ose kështu:

"Append  (cost=868.60..878.95 rows=2 width=233) (actual time=0.024..0.144 rows=2 loops=1)"
"  Buffers: shared hit=3"
"  CTE cl"
"    ->  Seq Scan on pg_class  (cost=0.00..868.60 rows=9972 width=537) (actual time=0.016..0.042 rows=101 loops=1)"
"          Buffers: shared hit=3"
"  ->  Limit  (cost=0.00..0.10 rows=1 width=233) (actual time=0.023..0.024 rows=1 loops=1)"
"        Buffers: shared hit=1"
"        ->  CTE Scan on cl  (cost=0.00..997.20 rows=9972 width=233) (actual time=0.021..0.021 rows=1 loops=1)"
"              Buffers: shared hit=1"
"  ->  Limit  (cost=10.00..10.10 rows=1 width=233) (actual time=0.117..0.118 rows=1 loops=1)"
"        Buffers: shared hit=2"
"        ->  CTE Scan on cl cl_1  (cost=0.00..997.20 rows=9972 width=233) (actual time=0.001..0.104 rows=101 loops=1)"
"              Buffers: shared hit=2"
"Planning Time: 0.634 ms"
"Execution Time: 0.248 ms"

Por leximi i planit si tekst «në letër» është shumë i vështirë dhe i paqartë:

  • nĂ« nyjĂ« shfaqet shuma sipas burimeve tĂ« nĂ«npĂ«rgjithĂ«
    do tĂ« thotĂ« qĂ« pĂ«r tĂ« kuptuar sa kohĂ« u desh pĂ«r tĂ« ekzekutuar njĂ« nyje tĂ« caktuar, ose sa tĂ« dhĂ«na u lexuan nga e dhĂ«na nĂ« disk — duhet tĂ« zbresĂ«sh njĂ« nga tjetrin
  • koha e nyjĂ«s duhet tĂ« shumĂ«zohet me loops
    po, zbritja nuk Ă«shtĂ« operacioni mĂ« i vĂ«shtirĂ« qĂ« duhet bĂ«rĂ« ‘nĂ« mendje’ — pasi koha e ekzekutimit jepet e mesatarizuar pĂ«r njĂ« ekzekutim tĂ« nyjĂ«s, dhe ato mund tĂ« jenĂ« qindra
  • pra, e gjithĂ« kjo bashkĂ« pengon pĂ«rgjigjen nĂ« pyetjen kryesore — kush Ă«shtĂ« ‘nyja mĂ« e dobĂ«t’?

Kur përpoqëm të shpjegonim të gjitha këto disa qindra zhvilluesve tanë, kuptuam se nga jashtë duket në këtë mënyrë:

Për çfarë nuk flet EXPLAIN, dhe si ta bëjmë atë të flasë

Pra, na nevojitet...

Mjeti

NĂ« tĂ«, pĂ«rpiqemi tĂ« mbledhim tĂ« gjitha mekanizmat kryesorĂ« qĂ« ndihmojnĂ« sipas planit dhe kĂ«rkesĂ«s pĂ«r tĂ« kuptuar, ‘kush Ă«shtĂ« fajtori dhe çfarĂ« tĂ« bĂ«jmë’. Po ashtu, dĂ«shirojmĂ« tĂ« ndajmĂ« pjesĂ« tĂ« pĂ«rvojĂ«s sonĂ« me komunitetin.
MirĂ« se vini dhe shfrytĂ«zojeni — explain.tensor.ru

Qartësia e planeve

A është e lehtë të kuptohet plani kur duket kështu?

Seq Scan on pg_class (actual time=0.009..1.304 rows=6609 loops=1)\n  Buffers: shared hit=263\nPlanning Time: 0.108 ms\nExecution Time: 1.800 ms

Jo shumë.

Por kĂ«shtu, nĂ« njĂ« formĂ« tĂ« pĂ«rmbledhur, kur treguesit kryesorĂ« janĂ« tĂ« ndarĂ« — tashmĂ« Ă«shtĂ« shumĂ« mĂ« e qartĂ«:

Për çfarë nuk flet EXPLAIN, dhe si ta bëjmë atë të flasë

Por nĂ«se plani Ă«shtĂ« mĂ« i komplikuar — nĂ« ndihmĂ« vjen grafiku i shpĂ«rndarjes sĂ« kohĂ«s pĂ«r nyjat:

Për çfarë nuk flet EXPLAIN, dhe si ta bëjmë atë të flasë

Por, për variantet më të komplikuara, në ndihmë vjen diagrami i ekzekutimit:

Për çfarë nuk flet EXPLAIN, dhe si ta bëjmë atë të flasë

Për shembull, ndodhin situata të mjaftueshme jo triviale, kur plani mund të ketë më shumë se një rrënjë të faktike:

Për çfarë nuk flet EXPLAIN, dhe si ta bëjmë atë të flasëPër çfarë nuk flet EXPLAIN, dhe si ta bëjmë atë të flasë

Këshillat strukturore

Por nĂ«se e gjithĂ« struktura e planit dhe vendet e saj tĂ« dobĂ«ta janĂ« tashmĂ« tĂ« renditura dhe tĂ« dukshme — pse tĂ« mos i ndriçojmĂ« ato pĂ«r zhvilluesin dhe t'i shpjegojmĂ« ‘nĂ« njĂ« gjuhĂ« tĂ« thjeshtë’?

Për çfarë nuk flet EXPLAIN, dhe si ta bëjmë atë të flasëKëto lloje rekomandimesh kemi mbledhur tashmë disa dhjetëra.

Profiler i rreshtit të kërkesës

Tani, nĂ«se mbi planin e analizuar ngeshen kĂ«rkesa origjinale, mund tĂ« shihni sa kohĂ« ka shkuar pĂ«r çdo operator tĂ« veçantĂ« — afĂ«rsisht kĂ«shtu:

Për çfarë nuk flet EXPLAIN, dhe si ta bëjmë atë të flasë


 ose madje kështu:

Për çfarë nuk flet EXPLAIN, dhe si ta bëjmë atë të flasë

Zëvendësimi i parametrave në kërkesë

NĂ«se ‘keni lidhur’ me planin jo vetĂ«m kĂ«rkesĂ«n, por edhe parametrat e saj nga rreshti DETAIL tĂ« regjistrit, atĂ«herĂ« mund ta kopjoni atĂ« gjithashtu nĂ« njĂ« nga variantet:

  • me zĂ«vendĂ«simin e vlerave nĂ« kĂ«rkesĂ«
    për ekzekutim të drejtpërdrejt në bazën tuaj dhe më pas profilimin e saj
    SELECT 'const', 'param'::text;
  • me zĂ«vendĂ«simin e vlerave pĂ«rmes PREPARE/EXECUTE
    pĂ«r tĂ« emuluar funksionimin e planifikuesit, kur pjesa parametrike mund tĂ« injorohet — p.sh., gjatĂ« punĂ«s me tabela tĂ« ndara
    DEALLOCATE ALL;\nPREPARE q(text) AS SELECT 'const', $1::text;\nEXECUTE q('param'::text);
    

Arkivi i planeve

Shtoni, analizoni, ndahuni me kolegët! Planet do të qëndrojnë në arkiv dhe do të mund të ktheheni te ato më vonë: explain.tensor.ru/archive

Por nĂ«se nuk dĂ«shironi qĂ« plani juaj tĂ« shfaqet nga tĂ« tjerĂ«t, mos harroni tĂ« shĂ«noni kutinĂ« ‘mos e publikoni nĂ« arkiv’.

Në artikujt e ardhshëm do të flas për ato sfida dhe zgjidhje që shfaqen gjatë analizës së planit.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster