PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Un accenno a PostgreSQL

La guerra infuocata di questa settimana sull'elenco delle prestazioni di pgsql ruota ancora una volta attorno al fatto che PostgreSQL non ha la tradizionale sintassi dei suggerimenti disponibile in altri database. Ci sono un mix di ragioni tecniche e pragmatiche dietro perché questo è:

  • L'introduzione di suggerimenti è una fonte comune di problemi successivi, perché correggere un punto di query una volta in un caso speciale non è un approccio molto affidabile. Man mano che il tuo set di dati cresce e forse cambia anche la distribuzione, l'idea a cui hai accennato quando era piccola può diventare un'idea sempre più cattiva.
  • L'aggiunta di un'interfaccia di suggerimento utile complicherebbe il codice dell'ottimizzatore, che è già abbastanza difficile da mantenere così com'è. Parte del motivo per cui PostgreSQL funziona così come esegue le query è perché il codice di benessere ("possiamo controllare i suggerimenti sul nostro elenco di funzionalità di confronto dei fornitori!") Che in realtà non si ripaga da solo, in termini di creazione del database sufficientemente migliore da giustificare il suo continuo mantenimento, è respinto dalla politica. Se non funziona, non verrà aggiunto. E se valutati oggettivamente, i suggerimenti sono in media un problema piuttosto che una soluzione.
  • Il tipo di problemi che i suggerimenti funzionano possono essere bug dell'ottimizzatore. La community di PostgreSQL risponde ai veri bug nell'ottimizzatore più velocemente di chiunque altro nel settore. Chiedi in giro e non devi incontrare molti utenti di PostgreSQL prima di trovarne uno che abbia segnalato un bug e abbia visto che è stato risolto il giorno successivo.

Ora, la principale risposta completamente valida per scoprire i suggerimenti che mancano, normalmente dai DBA che sono abituati a loro, è "bene, come faccio a gestire un bug dell'ottimizzatore quando mi imbatto in esso?" Come tutto il lavoro tecnologico al giorno d'oggi, di solito c'è un'enorme pressione per ottenere la soluzione più rapida possibile quando si verifica un problema con una query errata.
Se PostgreSQL non avesse dei modi per affrontare quella situazione, non ci sarebbero database PostgreSQL di produzione seri . La differenza è che le cose che modifichi in questo database sono più radicate nell'influenzare le decisioni che l'ottimizzatore prende già in un modo abbastanza sottile, piuttosto che semplicemente dirgli cosa fare. Questi sono suggerimenti nel senso letterale della parola, semplicemente non hanno l'interfaccia utente per suggerire che gli utenti di altri database nuovi in ​​PostgreSQL stiano cercando.
Con questo in mente, diamo un'occhiata a cosa puoi fare in PostgreSQL per aggirare i piani di query errati e i bug dell'ottimizzatore, in particolare le cose che molte persone sembrano pensare possano essere risolte solo con suggerimenti:

  • join_collapse_limit: questo regola la flessibilità che l'ottimizzatore ha per riordinare i join di più tabelle. Normalmente prova tutte le possibili combinazioni quando i join possono essere riorganizzati (che è la maggior parte delle volte, a meno che tu non stia usando un join esterno). Abbassare join_collapse_limit, magari anche a 1, rimuove parte o tutta questa flessibilità. Con impostato su 1, otterrai i join nell'ordine in cui li hai scritti, punto. La pianificazione di un gran numero di join è una delle cose più difficili da fare per l'ottimizzatore; ogni join amplifica gli errori nelle stime e aumenta il tempo di pianificazione delle query. Se la natura sottostante dei tuoi dati rende ovvio quale order join dovrebbe avvenire e non ti aspetti che cambi mai, una volta individuato l'ordine corretto puoi bloccarlo utilizzando questo parametro.
  • random_page_cost: per impostazione predefinita, questo parametro imposta il costo della ricerca su disco per trovare una pagina casuale su disco, rispetto a un valore di riferimento di 1,0. Ora, in realtà, se misuri il rapporto tra I/O casuale e sequenziale sui normali dischi rigidi, scoprirai che questo numero è più vicino a 50.  Allora perché 4,0? Primo, perché ha funzionato meglio di valori più grandi nei test della comunità. In secondo luogo, in molti casi, in particolare, i dati dell'indice verranno memorizzati nella cache, riducendo il costo effettivo della lettura di tali valori. Se, ad esempio, il tuo indice è memorizzato nella cache per il 90% nella RAM, significa che il 10% delle volte eseguirai l'operazione che è 50 volte più costosa; ciò renderebbe il tuo random_page_cost effettivo di circa 5.  Questo tipo di situazione del mondo reale è il motivo per cui l'impostazione predefinita ha senso dove si trova. Normalmente vedo che gli indici popolari ottengono>95% di cache in memoria. Se il tuo indice è in realtà molto più probabile che sia tutto nella RAM, ridurre random_page_cost fino a poco sopra 1.0 può essere una scelta ragionevole, per riflettere che non è più costoso di qualsiasi altra lettura. Allo stesso tempo, le ricerche casuali su sistemi molto occupati possono essere molto più costose delle aspettative che avresti solo guardando le simulazioni per utente singolo. Ho dovuto impostare random_page_cost a un massimo di 60 per far sì che il database smettesse di utilizzare gli indici quando il pianificatore stimava erroneamente quanto sarebbero stati costosi. In genere questa situazione deriva da un errore di stima della sensibilità da parte del pianificatore:se si esegue la scansione di più del 20% circa di una tabella, il pianificatore sa che l'utilizzo di una scansione sequenziale sarà molto più efficiente di una scansione dell'indice. La brutta situazione in cui ho dovuto forzare quel comportamento a verificarsi molto prima si è verificata quando il pianificatore si aspettava la restituzione dell'1% delle righe, ma in realtà era più vicino al 15%.
  • work_mem: regola la quantità di memoria disponibile per le query che eseguono operazioni di ordinamento, hashing e simili basate sulla memoria. Questa è solo una linea guida approssimativa per le query, non un limite rigido, e un singolo client può finire per utilizzare multipli di work_mem durante l'esecuzione di una query. Di conseguenza, è necessario fare attenzione a non impostare questo valore troppo alto nel file postgresql.conf. Quello che puoi fare invece, però, è impostarlo prima di eseguire una query che beneficia davvero della memoria aggiuntiva per contenere l'ordinamento o l'hash dei dati. A volte puoi trovare queste query registrando quelle lente usando log_min_duration_statement. Puoi anche trovarli attivando log_temp_files, che registrerà ogni volta che work_mem è troppo piccolo, e quindi le operazioni di ordinamento si riversano su disco invece di avvenire in memoria.
  • OFFSET 0:  PostgreSQL riorganizzerà le sottoquery nella forma di un join, in modo che possa quindi utilizzare la normale logica dell'ordine di join per ottimizzarlo. In alcuni casi, quella decisione può essere davvero pessima, poiché il genere di cose che le persone tendono a scrivere come sottoquery sembrano un po' più difficili da stimare per qualche motivo (lo dico in base al numero di query così fastidiose che vedo). Un trucco subdolo che puoi fare per prevenire questa logica è mettere OFFSET 0 alla fine della sottoquery. Ciò non modifica i risultati, ma l'inserimento del tipo di nodo di query Limit utilizzato per eseguire OFFSET impedirà il riarrangiamento. La sottoquery verrà quindi sempre eseguita nel modo in cui la maggior parte delle persone si aspetta, come nodo di query isolato.
  • enable_seqscan, enable_indexscan, enable_bitmapscan: La disattivazione di una di queste funzionalità per la ricerca di righe in una tabella è un martello abbastanza grande per consigliare vivamente di evitare quel tipo di scansione (non sempre prevenendolo, se non c'è modo di eseguire il tuo piano ma un seqscan, otterrai un seqscan anche se i parametri sono disattivati). La cosa principale per cui li consiglio non è correggere le query, è sperimentare con EXPLAIN e vedere perché è stato preferito l'altro tipo di scansione.
  • enable_nestloop, enable_hashjoin, enable_mergejoin: se sospetti che il tuo problema sia il tipo di join utilizzato piuttosto che il modo in cui le tabelle vengono lette, prova a disattivare il tipo che vedi nel tuo piano utilizzando uno di questi parametri, quindi esegui EXPLAIN ancora. Errori nelle stime di sensibilità possono facilmente far sembrare un join più o meno efficiente di quanto non sia in realtà. E, ancora, vedere come cambia il piano con l'attuale metodo di join disabilitato può essere molto istruttivo per il motivo per cui ha deciso in primo luogo quello.
  • enable_hashagg, enable_material:  queste funzionalità sono relativamente nuove per PostgreSQL. L'uso aggressivo dell'aggregazione hash è stato introdotto nella versione 8.4 e la materializzazione più aggressiva nella 9.0. Se vedi questi tipi di nodi nell'output di EXPLAIN
    e sembra che stiano facendo qualcosa di sbagliato, poiché questo codice è molto più recente è un po' più probabile che abbia una limitazione o un bug rispetto ad alcune delle funzionalità precedenti. Se avevi un piano che funzionava bene nelle versioni precedenti di PostgreSQL, ma utilizza uno di questi tipi di nodi e sembra funzionare molto peggio di conseguenza, la disabilitazione di queste funzionalità a volte può riportarti al comportamento precedente, oltre a far luce su perché l'ottimizzatore ha fatto la cosa sbagliata come feedback utile. Tieni presente che questo è generalmente il modo in cui le funzionalità più avanzate tendono ad essere introdotte in PostgreSQL: con la possibilità di disattivarlo per la risoluzione dei problemi, se si verifica una regressione del piano relativa al modo in cui le versioni precedenti eseguivano le cose.
  • cursor_tuple_fraction: se non intendi rileggere tutte le righe da una query, dovresti utilizzare un cursore per implementarlo. In tal caso, l'ottimizzatore cerca di stabilire la priorità se restituisce rapidamente la prima riga o se preferisce ottimizzare l'intera query, in base a questo parametro. Per impostazione predefinita, il database presuppone che leggerai nuovamente il 10% della query quando utilizzi un cursore. La regolazione di questo parametro ti consente di spostarlo in modo che ti aspetti di leggere di meno o di più.

Tutti questi parametri e le modifiche alle query dovrebbero essere considerati aggiustamenti del triage. Non vuoi correre con questi in posizione per sempre (tranne forse per join_collapse_limit). Li usi per uscire da un ingorgo, e poi si spera che tu possa capire quale sia la vera causa alla base del cattivo piano (statistiche errate, limitazione/bug dell'ottimizzatore o qualcos'altro) e quindi affrontare il problema da quella direzione. Più stai spingendo il comportamento dell'ottimizzatore in una direzione, più sei esposto a cambiamenti futuri nei tuoi dati, rendendo quella spinta non più corretta. Se li usi correttamente, come modo per studiare il motivo per cui hai sbagliato piano (l'approccio che ho usato nel capitolo sull'ottimizzazione delle query di PostgreSQL 9.0 High Performance), il modo in cui suggerisci le cose in PostgreSQL dovrebbe farti abbandonare ogni run- con un cattivo comportamento dell'ottimizzatore un po' più esperto su come evitare quella classe di problemi in futuro