2012-02-14 10 views
6

In PostgreSQL, quando sono pianificate le query (SELECT)?Quando sono pianificate le query (SELECT)?

E ':

  1. in fase di dichiarazione-preparare, o
  2. all'inizio della lavorazione del SELECT, o
  3. qualcos'altro

Il motivo che mi chiedo è che ci è una domanda StackOverflow: same query, two different ways, vastly different performance

Un sacco di gente sembra pensare che t la query è pianificata in modo diverso perché in un caso la query contiene una stringa letterale ('foo') e in un altro caso è un segnaposto (?).

Ora il mio pensiero è che questa è un'aringa rossa, perché la query non è pianificata al momento della preparazione dell'istruzione, ma in realtà è pianificata al momento SELECT.

Quindi, ad esempio, potrei preparare una dichiarazione con un segnaposto, quindi eseguire la query più volte con valori di limite diversi e il pianificatore di query verrà eseguito per ogni valore associato diverso.

sospetto che il question linked above si riduce al tipo di dati PostgreSQL del valore, che nel caso di un 'foo' letterale è noto per essere una stringa, ma nel caso di un segnaposto, il tipo non può essere divined , quindi sta arrivando al pianificatore di query come un tipo strano, per il quale non è possibile creare un piano efficiente. In tal caso, il problema non è che la query sia pianificata in modo diverso perché il valore è un segnaposto (al momento della preparazione dell'istruzione) di per sé ma che il valore sta giungendo alla query come un tipo PostgreSQL diverso e che è ciò che influenza il pianificatore di query. Per risolvere ciò sarebbe semplicemente una questione di legare il segnaposto con una dichiarazione di tipo esplicito appropriata.

+1

Buona domanda! Questo è molto specifico per DBMS; DBMS diversi escogitano risposte (leggermente) diverse. Il risultato netto può essere drammaticamente diverso. Informix supporta un 'OPEN cursor USING ... WITH REOPTIMIZATION' che non deve ripetere l'SQL (che risparmia tempo) ma fa rifare il piano di query per il set corrente di parametri. Questo è diverso di nuovo dalla maggior parte dei DBMS. –

+0

Sì. Questa domanda si sta rivelando sorprendentemente controversa.Abbiamo upvotes e un downvote sulla domanda e una delle risposte. – zgpmax

+0

@hochgurgler: il tuo sospetto sulla mancata corrispondenza di tipo è di per sé una preoccupazione valida, PostgreSQL _ha alcuni problemi noti qui. Ma considerando la prima parte della domanda, penso che questi due problemi non dovrebbero essere mescolati e che la seconda parte sarebbe meglio gestita da una domanda separata. La prego di dividerlo? –

risposta

10

Non riesco a parlare dell'interfaccia Perl lato client ma posso far luce sul lato server PostgreSQL.

PostgreSQL ha preparato istruzioni e dichiarazioni non preparate. Le istruzioni non preparate vengono analizzate, pianificate ed eseguite immediatamente. Fanno anche non sostituzione del parametro di supporto. Su una pianura psql shell è possibile mostrare il loro piano di query come questa:

tmpdb> explain select * from sometable where flag = true; 

D'altra parte ci sono le istruzioni preparate: Di solito sono (vedi "eccezione" di seguito) analizzato e progettato in un solo passaggio ed eseguito in un Secondo passo. Possono essere rieseguiti più volte con parametri diversi, perché sono do sostituzione del parametro di supporto.L'equivalente in psql è questo:

tmpdb> prepare foo as select * from sometable where flag = $1; 
tmpdb> explain execute foo(true); 

Si può vedere, che il piano è diverso dal piano nella dichiarazione impreparato, perché la pianificazione ha avuto luogo già in fase di prepare come descritto nel documento per PREPARE:

Quando viene eseguita l'istruzione PREPARE, l'istruzione specificata è analizzato, riscritto, e pianificato. Quando un comando EXECUTE è successivamente emesso, la dichiarazione preparata deve essere eseguita solo. Pertanto, le fasi di analisi, riscrittura e pianificazione vengono eseguite una volta sola, anziché ogni volta che viene eseguita l'istruzione.

Questo significa anche che il piano è NON ottimizzato per i parametri sostituiti: Nei primi esempi potrebbe utilizzare un indice per flag perché PostgreSQL sa che all'interno di un milione di voci solo dieci ha il valore true. Questo ragionamento è impossibile quando PostgreSQL usa una dichiarazione preparata. In tal caso viene creato un piano che funzionerà per tutti i possibili valori dei parametri nel miglior modo possibile. Questo potrebbe escludere l'indice menzionato perché il recupero della parte migliore della tabella completa tramite accesso casuale (a causa dell'indice) è più lento di una semplice scansione sequenziale. Il PREPARE doc conferma:

In alcune situazioni, il piano di query prodotta per una dichiarazione preparata sarà inferiore al piano di query che sarebbe stato scelto, se la dichiarazione è stata presentata ed eseguita normalmente. Questo perché quando l'istruzione è pianificata e il pianificatore tenta di determinare il piano di query ottimale, i valori effettivi di tutti i parametri specificati nell'istruzione non sono disponibili. PostgreSQL raccoglie statistiche sulla distribuzione dei dati nella tabella e può utilizzare valori costanti in una dichiarazione per fare ipotesi sul probabile risultato dell'esecuzione dell'istruzione. Poiché questi dati non sono disponibili quando si pianificano istruzioni preparate con parametri, il piano scelto potrebbe non essere ottimale.

proposito - quanto riguarda il piano cache il PREPARE doc anche ha qualcosa da dire:

Le dichiarazioni preparate durano solo per la durata della sessione di database corrente. Al termine della sessione, l'istruzione preparata viene dimenticata, quindi deve essere ricreata prima di essere utilizzata nuovamente.

Inoltre non è disponibile la memorizzazione automatica nella cache del piano e nessuna memorizzazione nella cache/riutilizzo su più connessioni.

ECCEZIONE: ho menzionato "di solito". Gli esempi psql mostrati non sono quelli che un adattatore client come Perl DBI usa davvero. Usa un certo protocol. Qui il termine "query semplice" corrisponde alla "query non preparata" in psql, il termine "extended query" corrisponde a "query preparata" con un'eccezione: esiste una distinzione tra (uno) "istruzione senza nome" e (eventualmente più) " dichiarazioni con nome ". Per quanto riguarda le dichiarazioni nominato il doc dice:

istruzioni preparate con nome possono essere creati e accessibili a livello di comando SQL, usando preparazione ed esecuzione.

e anche: pianificazione

query per gli oggetti preparati dichiarazione di nome si verifica quando il messaggio viene elaborato Parse.

Quindi in questo caso la pianificazione viene eseguita senza parametri come descritto sopra per PREPARE - niente di nuovo.

L'eccezione menzionata è la "dichiarazione senza nome". Il dottore dice:

La dichiarazione preparata senza nome è altresì prevista durante l'elaborazione di analisi se il messaggio Parse definisce parametri. Ma se ci sono dei parametri, la pianificazione delle query avviene ogni volta che vengono forniti i parametri Bind. Ciò consente al pianificatore di utilizzare i valori effettivi dei parametri forniti da ciascun messaggio Bind, anziché utilizzare stime generiche.

E questo è il vantaggio: sebbene l'istruzione senza nome sia "preparata" (cioè può avere sostituzione di parametro), può anche adattare il piano di query ai parametri effettivi.

BTW: la gestione esatta dell'istruzione senza nome è stata modificata più volte nelle versioni precedenti del server PostgreSQL. Puoi cercare i vecchi documenti per i dettagli, se proprio vuoi.

Razionale - Perl/qualsiasi client:

Come un client come Perl utilizza il protocollo è una questione del tutto diversa. Alcuni client come il driver JDBC per Java in pratica dicono: anche se il programmatore utilizza una dichiarazione preparata, le prime cinque (o così) esecuzioni sono mappate internamente a una "query semplice" (cioè effettivamente non preparata), dopo che il driver passa a " dichiarazione con nome ".

Quindi un cliente ha queste scelte:

  • Force (ri) pianificazione di volta in volta utilizzando il protocollo "semplice query".
  • Piano una volta, eseguire più volte utilizzando il protocollo "query estesa" e la "dichiarazione denominata" (il piano potrebbe essere negativo perché la pianificazione viene eseguita senza parametri).
  • Parse una volta, il piano per ogni esecuzione (con la versione di PostgreSQL corrente) utilizzando il protocollo "interrogazione estesa" e la "dichiarazione senza nome" e obbedendo alcune altre cose (fornire alcuni params con messaggio di "analizzare")
  • Gioca a trucchi completamente diversi come il driver JDBC.

Cosa fa Perl attualmente: non lo so. Ma la menzionata "falsa pista" non è molto improbabile.

+0

+1 ma ne vale la pena! –

+0

+1 Molto informativo. –

+0

Semi-aggiornamento: dalla versione 9.2 la pianificazione è leggermente diversa. Fondamentalmente verranno pianificate anche query preparate per ogni chiamata utilizzando i parametri attuali. Solo dopo aver provato diverse invocazioni, che i piani non differiscono molto, viene utilizzato un piano generico. –

Problemi correlati