Domanda

L'output di spiegazione di MySQL è piuttosto semplice. PostgreSQL è un po 'più complicato. Non sono stato in grado di trovare una buona risorsa che lo spieghi neanche.

Puoi descrivere cosa sta esattamente dicendo o indirizzarmi nella direzione di una buona risorsa?

È stato utile?

Soluzione

Explaining_EXPLAIN.pdf potrebbe essere di aiuto.

Altri suggerimenti

La parte che ho sempre trovato confuso è il costo di avvio rispetto al costo totale. Lo Google ogni volta che me ne dimentico, il che mi riporta a qui, il che non spiega la differenza, motivo per cui sto scrivendo questa risposta. Questo è ciò che ho raccolto dalla Postgres EXPLAIN documentazione , spiegato come ho capito.

Ecco un esempio di un'applicazione che gestisce un forum:

EXPLAIN SELECT * FROM post LIMIT 50;

Limit  (cost=0.00..3.39 rows=50 width=422)
  ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)

Ecco la spiegazione grafica di PgAdmin:

 spiegazione grafica della prima query

(Quando si utilizza PgAdmin, è possibile puntare il mouse su un componente per leggere i dettagli del costo.)

Il costo è rappresentato come una tupla, ad es. il costo di LIMIT è cost=0.00..3.39 e il costo della scansione sequenziale post è cost=0.00..15629.12. Il primo numero nella tupla è il costo di avvio e il secondo numero è il costo totale . Poiché ho usato EXPLAIN ANALYZE e non Seq Scan, questi costi sono stime, non misure effettive.

  • Costo di avvio è un concetto complicato. Non rappresenta solo il tempo che deve trascorrere prima che il componente inizi . Rappresenta il lasso di tempo tra quando il componente inizia l'esecuzione (lettura dei dati) e quando il componente genera la prima riga .
  • Costo totale è l'intero tempo di esecuzione del componente, da quando inizia a leggere i dati a quando termina di scrivere il suo output.

Come complicazione, ogni " parent " I costi del nodo includono i costi dei suoi nodi figlio. Nella rappresentazione del testo, l'albero è rappresentato da rientro, ad es. 3.39 è un nodo padre e 15629.12 è suo figlio. Nella rappresentazione PgAdmin, le frecce puntano da figlio a genitore & # 8212; la direzione del flusso di dati & # 8212; che potrebbe essere controintuitivo se hai familiarità con la teoria dei grafi.

La documentazione afferma che i costi sono comprensivi di tutti i nodi figlio, ma si noti che il costo totale del padre EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2; è molto più piccolo del costo totale del figlio ORDER BY. Il costo totale non è incluso perché un componente come 23283.24 non deve elaborare l'intero input. Vedi l'esempio 23859.27 nella Postgres <=> documentazione .

Nell'esempio sopra, il tempo di avvio è zero per entrambi i componenti, poiché nessuno dei due componenti deve eseguire alcuna elaborazione prima di iniziare a scrivere le righe: una scansione sequenziale legge la prima riga della tabella e la emette. <=> legge la sua prima riga e quindi la emette.

Quando un componente dovrebbe eseguire molta elaborazione prima di poter iniziare a produrre righe? Ci sono molte ragioni possibili, ma diamo un'occhiata a un chiaro esempio. Ecco la stessa query precedente ma che ora contiene una <=> clausola:

EXPLAIN SELECT * FROM post ORDER BY body LIMIT 50;

Limit  (cost=23283.24..23283.37 rows=50 width=422)
  ->  Sort  (cost=23283.24..23859.27 rows=230412 width=422)
        Sort Key: body
        ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)

E graficamente:

 spiegazione grafica della seconda query

Ancora una volta, la scansione sequenziale su <=> non ha costi di avvio: inizia a produrre righe immediatamente. Ma l'ordinamento ha un costo di avvio significativo <=> perché deve ordinare l'intera tabella prima di poter generare anche una sola riga . Il costo totale dell'ordinamento <=> è solo leggermente superiore al costo di avvio, a causa del fatto che una volta che l'intero set di dati è stato ordinato, i dati ordinati possono essere emessi molto rapidamente.

Si noti che il tempo di avvio di <=> <=> è esattamente uguale al tempo di avvio dell'ordinamento. Questo non perché <=> stesso ha un tempo di avvio elevato. In realtà ha zero tempo di avvio da solo, ma <=> arrotola tutti i costi figlio per ciascun genitore, quindi il <=> tempo di avvio include tsomma i tempi di avvio dei suoi figli.

Questo riepilogo dei costi può rendere difficile comprendere il costo di esecuzione di ogni singolo componente. Ad esempio, il nostro <=> ha zero tempi di avvio, ma non è ovvio a prima vista. Per questo motivo, molte altre persone collegate a spiegato.depesz.com , uno strumento creato da Hubert Lubaczewski (aka depesz ) che aiuta a comprendere <=> con & # 8212; tra l'altro & # 8212; sottraendo i costi del figlio dai costi del genitore. Cita alcune altre complessità in un breve post sul blog sul suo strumento.

Esegue dalla più indentata alla meno indentata, e credo dal fondo del piano verso l'alto. (Quindi se ci sono due sezioni rientrate, una più in basso nella pagina viene eseguita per prima, quindi quando si incontrano le altre vengono eseguite, quindi viene eseguita la regola che le unisce.)

L'idea è che ad ogni passo ci sono 1 o 2 set di dati che arrivano e vengono elaborati da una regola. Se solo un set di dati, tale operazione viene eseguita per quel set di dati. (Ad esempio, scansiona un indice per capire quali righe desideri, filtra un set di dati o ordinalo.) Se due, i due set di dati sono le due cose che sono rientrate ulteriormente e sono unite dalla regola che vedi. Il significato della maggior parte delle regole può essere ragionevolmente facilmente indovinato (in particolare se hai già letto un sacco di spiegazioni di piani in precedenza), tuttavia puoi provare a verificare singoli elementi o cercando nella documentazione o (più facilmente) semplicemente inserendo la frase in Google insieme ad alcune parole chiave come EXPLAIN.

Questa ovviamente non è una spiegazione completa, ma fornisce un contesto sufficiente che di solito puoi capire quello che vuoi. Ad esempio, considera questo piano da un database reale:

explain analyze
select a.attributeid, a.attributevalue, b.productid
from orderitemattribute a, orderitem b
where a.orderid = b.orderid
and a.attributeid = 'display-album'
and b.productid = 'ModernBook';

------------------------------------------------------------------------------------------------------------------------------------------------------------

 Merge Join  (cost=125379.14..125775.12 rows=3311 width=29) (actual time=841.478..841.478 rows=0 loops=1)
   Merge Cond: (a.orderid = b.orderid)
   ->  Sort  (cost=109737.32..109881.89 rows=57828 width=23) (actual time=736.163..774.475 rows=16815 loops=1)
         Sort Key: a.orderid
         Sort Method:  quicksort  Memory: 1695kB
         ->  Bitmap Heap Scan on orderitemattribute a  (cost=1286.88..105163.27 rows=57828 width=23) (actual time=41.536..612.731 rows=16815 loops=1)
               Recheck Cond: ((attributeid)::text = 'display-album'::text)
               ->  Bitmap Index Scan on (cost=0.00..1272.43 rows=57828 width=0) (actual time=25.033..25.033 rows=16815 loops=1)
                     Index Cond: ((attributeid)::text = 'display-album'::text)
   ->  Sort  (cost=15641.81..15678.73 rows=14769 width=14) (actual time=14.471..16.898 rows=1109 loops=1)
         Sort Key: b.orderid
         Sort Method:  quicksort  Memory: 76kB
         ->  Bitmap Heap Scan on orderitem b  (cost=310.96..14619.03 rows=14769 width=14) (actual time=1.865..8.480 rows=1114 loops=1)
               Recheck Cond: ((productid)::text = 'ModernBook'::text)
               ->  Bitmap Index Scan on id_orderitem_productid  (cost=0.00..307.27 rows=14769 width=0) (actual time=1.431..1.431 rows=1114 loops=1)
                     Index Cond: ((productid)::text = 'ModernBook'::text)
 Total runtime: 842.134 ms
(17 rows)

Prova a leggerlo da solo e vedi se ha senso.

Quello che ho letto è che il database prima scansiona l'indice id_orderitem_productid, usando quello per trovare le righe che vuole da orderitem, quindi ordina quel set di dati usando un quicksort (l'ordinamento usato cambierà se i dati non si adattano in RAM), quindi lo mette da parte.

Successivamente, analizza orditematt_attributeid_idx per trovare le righe desiderate da orderitemattribute e quindi ordina quel set di dati utilizzando un quicksort.

Prende quindi i due set di dati e li unisce. (Un join di unione è una sorta di & Quot; zippare & Quot; operazione in cui percorre i due set di dati ordinati in parallelo, emettendo la riga unita quando corrispondono.)

Come ho detto, lavori attraverso la parte interna del piano verso la parte esterna, dal basso verso l'alto.

È disponibile anche uno strumento di supporto online, Depesz , che evidenzierà dove le parti costose dell'analisi i risultati sono.

ne ha anche uno, ecco i stessi risultati , che per me rendono più chiaro dove si trova il problema è.

PgAdmin ti mostrerà una rappresentazione grafica del piano esplicativo. Passare avanti e indietro tra i due può davvero aiutarti a capire cosa significa la rappresentazione del testo. Tuttavia, se vuoi solo sapere cosa sta per fare, potresti essere in grado di utilizzare sempre la GUI.

La documentazione ufficiale di PostgreSQL fornisce una spiegazione interessante e approfondita su come comprendere l'output di spiegare .

Se installi pgadmin, c'è un pulsante Spiega che oltre a fornire l'output del testo disegna diagrammi di ciò che sta accadendo, mostrando i filtri, gli ordinamenti e le unioni di sottoinsiemi che trovo davvero utili per vedere cosa sta succedendo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top