Come costringo Postgres a utilizzare un indice quando altrimenti insisterebbe per eseguire una scansione sequenziale?
Come costringo Postgres a utilizzare un indice quando altrimenti insisterebbe per eseguire una scansione sequenziale?
Risposte:
Supponendo che tu stia chiedendo informazioni sulla comune funzionalità di "suggerimento indice" che si trova in molti database, PostgreSQL non fornisce tale funzionalità. Questa è stata una decisione consapevole presa dal team di PostgreSQL. Una buona panoramica del perché e di cosa puoi fare invece può essere trovata qui . Le ragioni sono fondamentalmente che si tratta di un hack delle prestazioni che tende a causare più problemi in seguito quando i dati cambiano, mentre l'ottimizzatore di PostgreSQL può rivalutare il piano in base alle statistiche. In altre parole, quello che oggi potrebbe essere un buon piano di query probabilmente non sarà un buon piano di query per sempre e i suggerimenti sull'indice forzano un particolare piano di query per sempre.
Come un martello molto smussato, utile per i test, puoi usare i parametri enable_seqscan
e enable_indexscan
. Vedere:
Questi non sono adatti per l'uso in produzione continua . In caso di problemi con la scelta del piano di query, dovresti consultare la documentazione per individuare i problemi di prestazioni delle query . Non enable_
limitarti a impostare i parametri e andare via.
A meno che tu non abbia una buona ragione per usare l'indice, Postgres potrebbe fare la scelta corretta. Perché?
Vedi anche questo vecchio post di newsgroup .
Probabilmente l'unico motivo valido per l'utilizzo
set enable_seqscan=false
è quando scrivi query e vuoi vedere rapidamente quale sarebbe effettivamente il piano di query se ci fossero grandi quantità di dati nelle tabelle. O, naturalmente, se è necessario confermare rapidamente che la query non utilizza un indice semplicemente perché il set di dati è troppo piccolo.
set enable_seqscan=false
, esegui la tua query e poi corri velocemente set enable_seqscan=true
per riportare postgresql al suo comportamento corretto (e ovviamente non farlo in produzione, solo in fase di sviluppo!)
SET SESSION enable_seqscan=false
per influenzare solo te stesso
A volte PostgreSQL non riesce a fare la scelta migliore di indici per una particolare condizione. Ad esempio, supponiamo che ci sia una tabella delle transazioni con diversi milioni di righe, di cui ce ne sono diverse centinaia per un dato giorno, e la tabella ha quattro indici: transaction_id, client_id, date e description. Si desidera eseguire la seguente query:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description = 'Refund'
GROUP BY client_id
PostgreSQL può scegliere di utilizzare l'indice transaction_description_idx invece di transaction_date_idx, il che può portare la query a impiegare diversi minuti invece di meno di un secondo. Se questo è il caso, puoi forzare l'utilizzo dell'indice alla data eludendo la condizione in questo modo:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description||'' = 'Refund'
GROUP BY client_id
your_wanted_index
, può essere così che il motore postgresql eseguirà invece una scansione della sequenza / chiave primaria. Conclusione: non esiste un metodo affidabile al 100% per forzare l'utilizzo degli indici per il server PostgreSql.
where
condizioni ma due tabelle o unite e Postgres non riesce a prendere l'indice.
Questo problema si verifica in genere quando il costo stimato di una scansione dell'indice è troppo alto e non riflette correttamente la realtà. Potrebbe essere necessario abbassare il random_page_cost
parametro di configurazione per risolvere questo problema. Dalla documentazione di Postgres :
La riduzione di questo valore [...] farà sì che il sistema preferisca le scansioni dell'indice; alzarlo farà sembrare le scansioni dell'indice relativamente più costose.
Puoi verificare se un valore inferiore farà effettivamente utilizzare a Postgres l'indice (ma usalo solo per i test ):
EXPLAIN <query>; # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>; # May use index scan now
È possibile ripristinare SET random_page_cost = DEFAULT;
nuovamente il valore predefinito con .
Le scansioni degli indici richiedono il recupero delle pagine del disco non sequenziale. Postgres utilizza random_page_cost
per stimare il costo di tali recuperi non sequenziali in relazione ai recuperi sequenziali. Il valore predefinito è 4.0
, quindi supponendo un fattore di costo medio di 4 rispetto ai recuperi sequenziali (tenendo conto degli effetti della cache).
Il problema tuttavia è che questo valore predefinito non è adatto nei seguenti importanti scenari di vita reale:
1) Unità a stato solido
Come ammette la documentazione:
Lo storage che ha un basso costo di lettura casuale rispetto alle unità sequenziali, ad esempio unità a stato solido, potrebbe essere modellato meglio con un valore inferiore per
random_page_cost
.
Secondo l'ultimo punto di questa diapositiva da un intervento al PostgresConf 2018, random_page_cost
dovrebbe essere impostato su qualcosa tra 1.0
e 2.0
per le unità a stato solido.
2) Dati memorizzati nella cache
Se i dati dell'indice richiesti sono già memorizzati nella cache della RAM, una scansione dell'indice sarà sempre molto più veloce di una scansione sequenziale. La documentazione dice:
Di conseguenza, se è probabile che i tuoi dati siano completamente nella cache, [...]
random_page_cost
può essere appropriato ridurli.
Il problema è che ovviamente non puoi sapere facilmente se i dati rilevanti sono già memorizzati nella cache. Tuttavia, se viene richiesto frequentemente un indice specifico e se il sistema dispone di RAM sufficiente, è probabile che i dati vengano memorizzati nella cache erandom_page_cost
dovrebbero essere impostati su un valore inferiore. Dovrai sperimentare valori diversi e vedere cosa funziona per te.
Potresti anche voler usare l' estensione pg_prewarm per la cache dei dati esplicita.
La domanda di per sé è decisamente invalida. Forzare (facendo enable_seqscan = off per esempio) è una pessima idea. Potrebbe essere utile verificare se sarà più veloce, ma il codice di produzione non dovrebbe mai usare questi trucchi.
Invece, spiega l'analisi della tua query, leggila e scopri perché PostgreSQL sceglie un piano (secondo te) sbagliato.
Ci sono strumenti sul web che aiutano a leggere, spiegare, analizzare l'output - uno di questi è explore.depesz.com - scritto da me.
Un'altra opzione è quella di entrare nel canale #postgresql sulla rete irc freenode e parlare con i ragazzi lì per aiutarti - poiché l'ottimizzazione della query non è una questione di "fai una domanda, ricevi una risposta sii felice". è più come una conversazione, con molte cose da controllare, molte cose da imparare.
C'è un trucco per spingere postgres a preferire un seqscan che aggiunge un OFFSET 0
nella sottoquery
Questo è utile per ottimizzare le richieste che collegano tabelle grandi / enormi quando tutto ciò di cui hai bisogno sono solo gli n primi / ultimi elementi.
Diciamo che stai cercando i primi / ultimi 20 elementi che coinvolgono più tabelle con 100k (o più) voci, non ha senso costruire / collegare tutte le query su tutti i dati quando ciò che stai cercando è nei primi 100 o 1000 inserimenti. In questo scenario, ad esempio, risulta essere oltre 10 volte più veloce per eseguire una scansione sequenziale.
vedi Come posso impedire a Postgres di integrare una sottoquery?