2011-12-16 2 views
14

Sto cercando di ottimizzare una query complessa in PostgreSQL 9.1.2, che chiama alcune funzioni. Queste funzioni sono contrassegnate come STABLE o IMMUTABLE e vengono richiamate più volte con gli stessi argomenti nella query. Supponevo che PostgreSQL fosse abbastanza intelligente da chiamarli solo una volta per ogni set di input - dopotutto, questo è il punto di STABLE e IMMUTABLE, non è vero? Ma sembra che le funzioni vengano chiamate più volte. Ho scritto una semplice funzione per testare questo, che lo conferma:Perché PostgreSQL chiama la mia funzione STABILE/IMMUTABILE più volte?

CREATE OR REPLACE FUNCTION test_multi_calls1(one integer) 
RETURNS integer 
AS $BODY$ 
BEGIN 
    RAISE NOTICE 'Called with %', one; 
    RETURN one; 
END; 
$BODY$ LANGUAGE plpgsql IMMUTABLE; 


WITH data AS 
(
    SELECT 10 AS num 
    UNION ALL SELECT 10 
    UNION ALL SELECT 20 
) 
SELECT test_multi_calls1(num) 
FROM data; 

uscita:

NOTICE: Called with 10 
NOTICE: Called with 10 
NOTICE: Called with 20 

Perché succede questo e come posso ottenere che per eseguire solo la funzione di una volta?

risposta

23

La seguente estensione del codice di prova è informativo:

CREATE OR REPLACE FUNCTION test_multi_calls1(one integer) 
RETURNS integer 
AS $BODY$ 
BEGIN 
    RAISE NOTICE 'Immutable called with %', one; 
    RETURN one; 
END; 
$BODY$ LANGUAGE plpgsql IMMUTABLE; 
CREATE OR REPLACE FUNCTION test_multi_calls2(one integer) 
RETURNS integer 
AS $BODY$ 
BEGIN 
    RAISE NOTICE 'Volatile called with %', one; 
    RETURN one; 
END; 
$BODY$ LANGUAGE plpgsql VOLATILE; 

WITH data AS 
(
    SELECT 10 AS num 
    UNION ALL SELECT 10 
    UNION ALL SELECT 20 
) 
SELECT test_multi_calls1(num) 
FROM data 
where test_multi_calls2(40) = 40 
and test_multi_calls1(30) = 30 

USCITA:

NOTICE: Immutable called with 30 
NOTICE: Volatile called with 40 
NOTICE: Immutable called with 10 
NOTICE: Volatile called with 40 
NOTICE: Immutable called with 10 
NOTICE: Volatile called with 40 
NOTICE: Immutable called with 20 

Qui possiamo vedere che, mentre nella select-list la funzione immutabile è stato chiamato più volte, in la clausola where è stata chiamata una volta, mentre il volatile è stato chiamato tre volte.

La cosa importante non è che PostgreSQL chiamerà solo una funzione STABLE o IMMUTABLE una volta con gli stessi dati - il tuo esempio mostra chiaramente che questo non è il caso - è che possa chiamarla soltanto una volta. O forse lo chiamerà due volte quando dovrebbe chiamare una versione volatile 50 volte, e così via.

Esistono diversi modi in cui è possibile sfruttare la stabilità e l'immutabilità con costi e vantaggi diversi. Per fornire il tipo di salvataggio che stai suggerendo dovrebbe fare con select-lists dovrebbe mettere in cache i risultati, e quindi cercare ogni argomento (o elenco di argomenti) in questa cache prima di restituire il risultato memorizzato nella cache o chiamare la funzione su una cache -Perdere. Questo sarebbe più costoso che chiamare la tua funzione, anche nel caso in cui ci fosse un'alta percentuale di hit della cache (potrebbero esserci hit della cache pari allo 0%, il che significa che questa "ottimizzazione" ha fatto un lavoro extra senza alcun guadagno). Potrebbe memorizzare solo l'ultimo parametro e il risultato, ma di nuovo potrebbe essere completamente inutile.

Ciò è particolarmente vero considerando che le funzioni stabili e immutabili sono spesso le funzioni più leggere.

Con la clausola where tuttavia, l'immutabilità test_multi_calls1 permette PostgreSQL effettivamente ristrutturare le query dal significato piana del SQL proposta:

Per ogni riga calcolare test_multi_calls1 (30) e se il risultato è pari a 30, continuare l'elaborazione della riga in questione

per un diverso piano di query interamente:

Calcolare test_multi_calls1 (30) e se è uguale a 30 poi continuare con la query altrimenti restituisce una riga di zero set di risultati, senza ad ulteriori calcoli

Questo è il tipo di utilizzo che PostgreSQL rende di stabili e IMMUTABILE - non la memorizzazione nella cache dei risultati, ma la riscrittura delle query in query diverse che sono più efficienti ma danno gli stessi risultati.

Nota anche che test_multi_calls1 (30) viene chiamato prima di test_multi_calls2 (40) indipendentemente dall'ordine in cui appaiono nella clausola where. Ciò significa che se la prima chiamata non restituisce alcuna riga (sostituire = 30 con = 31 per il test), la funzione volatile non verrà richiamata affatto, sempre indipendentemente da quale sia il lato dello and.

Questo particolare tipo di riscrittura dipende dall'immutabilità o dalla stabilità. Con where test_multi_calls1(30) != num la riscrittura delle query avverrà per funzioni immutabili ma non per quelle semplicemente stabili. Con where test_multi_calls1(num) != 30 non avverrà affatto (chiamate multiple) anche se ci sono altre ottimizzazioni possibili:

Le espressioni contenenti solo funzioni STABILE e IMMUTABILE possono essere utilizzate con le scansioni di indice. Le espressioni che contengono le funzioni VOLATILI non possono. Il numero di chiamate può o non può diminuire, ma molto più importante il risultato delle chiamate verrà quindi utilizzato in un modo molto più efficiente nel resto della query (conta solo sui tavoli grandi, ma poi può fare un grande differenza).

In generale, non pensare alle categorie di volatilità in termini di memoria, ma piuttosto in termini di opportunità di pianificazione di query di PostgreSQL per ristrutturare intere query in modi che sono logicamente equivalenti (stessi risultati) ma molto più efficienti.

+0

Cose interessanti –

0

In base alle funzioni documentation IMMUTABLE restituirà lo stesso valore dato gli stessi argomenti. Dal momento che stai alimentando argomenti dinamici (e nemmeno lo stesso una volta) l'ottimizzatore non ha motivo di credere che otterrà gli stessi risultati e quindi chiama la funzione. Una migliore qualità è: perché la tua query invoca la funzione più volte se non è necessario?

+0

Anche se ho hardcode gli argomenti, ad es. 'SELECT test_multi_calls1 (10), test_multi_calls1 (10)' il messaggio viene ancora stampato due volte. Qual è il punto del modificatore 'IMMUTABLE' quindi? – EMP

+0

beh, se questo accade con argomenti hardcoded dovresti probabilmente contattare gli sviluppatori di PostgreSQL. –

+1

Riesci a pensare a un caso in cui 'SELECT f (x), f (x)' ha qualche valore pratico oltre a dimostrare che 'f (x)' è chiamato due volte? L'ottimizzatore di query costa cpu e memoria e deve guadagnare il suo mantenimento. Cercare casi del genere in ogni query nel caso in cui fossero presenti degraderebbe ogni query mai eseguita tranne quella. –