it-swarm.it

Performance delle funzioni

Proveniente da un background MySQL, in cui le procedure memorizzate prestazioni (articolo precedente) e sabilità sono discutibili, sto valutando PostgreSQL per un nuovo prodotto per la mia azienda.

Una delle cose che vorrei fare è spostare alcune delle logica dell'applicazione nelle procedure memorizzate, quindi sto qui chiedendo DO e NON (migliori pratiche) sull'uso delle funzioni in PostgreSQL (9.0 ), in particolare per quanto riguarda le insidie ​​prestazionali.

53
Derek Downey

A rigor di termini, il termine "stored procedure" indica procedure SQL in Postgres, introdotto con Postgres 11 . Relazionato:

Ci sono anche funzioni, che fanno quasi ma non proprio lo stesso, e quelle sono state lì dal inizio.

Le funzioni con LANGUAGE sql Sono fondamentalmente solo file batch con semplici comandi SQL in un wrapper di funzioni (e quindi atomici, sempre eseguiti all'interno di a single transazione) accettando i parametri. Tutte le istruzioni in una funzione SQL sono pianificate subito, che è leggermente diverso dall'esecuzione di un'istruzione dopo l'altra e può influenzare l'ordine in cui vengono eseguiti i blocchi.

Per di più, il linguaggio più maturo è PL/pgSQL (LANGUAGE plpgsql) . Funziona bene ed è stato migliorato con ogni versione nell'ultimo decennio, ma serve come colla per i comandi SQL. Non è pensato per calcoli pesanti (tranne che con i comandi SQL).

Le funzioni PL/pgSQL eseguono query come istruzioni preparate . Il riutilizzo dei piani di query memorizzati nella cache elimina alcune spese generali di pianificazione e le rende un po 'più veloci delle equivalenti istruzioni SQL, il che può essere un effetto evidente a seconda delle circostanze. Potrebbe anche avere effetti collaterali come in questa domanda correlata:

Questo porta i vantaggi e gli svantaggi delle dichiarazioni preparate - come discusso nel manuale . Per query su tabelle con distribuzione irregolare dei dati e parametri variabili SQL dinamico con EXECUTE può funzionare meglio quando il guadagno derivante da un piano di esecuzione ottimizzato per i parametri specificati supera il costo della ripianificazione.

Poiché i piani di esecuzione generici di Postgres 9.2 sono ancora memorizzati nella cache per la sessione ma, citando il manuale :

Ciò si verifica immediatamente per le istruzioni preparate senza parametri; in caso contrario, si verifica solo dopo che cinque o più esecuzioni hanno prodotto piani la cui media dei costi stimati (incluso il sovraccarico di pianificazione) è più costosa della stima dei costi del piano generico.

Otteniamo il meglio da entrambi i mondi il più delle volte (meno un sovraccarico aggiunto) senza (ab) usando EXECUTE. Dettagli in Novità di PostgreSQL 9.2 del Wiki PostgreSQL .

Postgres 12 introduce la variabile server aggiuntiva plan_cache_mode per forzare piani generici o personalizzati. Per casi speciali, usare con cura.

Puoi vincere alla grande con le funzioni lato server che impediscono ulteriori round trip al server di database dalla tua applicazione. Chiedi al server di eseguire il più possibile in una volta sola e restituisce solo un risultato ben definito.

Evita l'annidamento di funzioni complesse, in particolare le funzioni di tabella (RETURNING SETOF record O TABLE (...)). Le funzioni sono scatole nere che si pongono come barriere di ottimizzazione per il planner delle query. Sono ottimizzati separatamente, non nel contesto della query esterna, il che rende la pianificazione più semplice, ma può risultare in piani tutt'altro che perfetti. Inoltre, le dimensioni dei costi e dei risultati delle funzioni non possono essere previste in modo affidabile.

exception a questa regola sono semplici funzioni SQL (LANGUAGE sql), Che possono essere "in linea" - se sono soddisfatte alcune condizioni preliminari . Maggiori informazioni su come funziona il planner delle query in questa presentazione di Neil Conway (elementi avanzati).

In PostgreSQL una funzione viene sempre eseguita automaticamente all'interno di una singola transazione . Tutto ha successo o niente. Se si verifica un'eccezione, viene eseguito il rollback di tutto. Ma c'è gestione degli errori ...

Questo è anche il motivo per cui le funzioni sono not esattamente "stored procedure" (anche se quel termine viene usato talvolta, in modo fuorviante). Alcuni comandi come VACUUM , CREATE INDEX CONCURRENTLY o CREATE DATABASE non può essere eseguito all'interno di un blocco transazioni, quindi non sono consentiti nelle funzioni. (Nemmeno nelle procedure SQL, a partire da Postgres 11. Che potrebbe essere aggiunto in seguito.)

Ho scritto migliaia di funzioni plpgsql nel corso degli anni.

55

Alcuni DO:

  • Utilizzare SQL come linguaggio delle funzioni quando possibile, poiché PG può incorporare le istruzioni
  • Usa IMMUTABLE/STABLE/VOLATILE correttamente, poiché PG può memorizzare nella cache i risultati se è immutabile o stabile
  • Utilizzare STRICT correttamente, poiché PG può restituire null solo se qualsiasi input è null invece di eseguire la funzione
  • Considerare PL/V8 quando non è possibile utilizzare SQL come linguaggio delle funzioni. È più veloce di PL/pgSQL in alcuni test non scientifici che ho eseguito
  • Utilizzare ASCOLTA/NOTIFICA per processi a esecuzione più lunga che possono verificarsi fuori transazione
  • Considerare l'utilizzo delle funzioni per implementare l'impaginazione poiché l'impaginazione basata su chiave può essere più veloce dell'impaginazione basata su LIMIT
  • Assicurati di testare le tue unità
12
Neil McGuigan

In generale, spostare la logica dell'applicazione nel database significa che è più veloce, dopotutto verrà eseguito più vicino ai dati.

Credo (ma non sono sicuro al 100%) che funzioni del linguaggio SQL siano più veloci di quelli che usano altre lingue perché non richiedono il cambio di contesto. Il rovescio della medaglia è che non è consentita alcuna logica procedurale.

PL/pgSQL è il più maturo e completo di funzionalità dei linguaggi incorporati - ma per prestazioni, C può essere usato ( anche se gioverà solo a funzioni ad alta intensità computazionale)

Puoi fare alcune cose molto interessanti usando le funzioni definite dall'utente (UDF) in postgresql. Ad esempio, ci sono dozzine di lingue possibili che puoi usare. Il costruito in pl/sql e pl/pgsql sono entrambi capaci e affidabili e usano un metodo sandbox per impedire agli utenti di fare qualcosa di terribilmente pericoloso. Gli UDF scritti in C offrono il massimo in termini di potenza e prestazioni, poiché funzionano nello stesso contesto del database stesso. Tuttavia, è come giocare con il fuoco, perché anche piccoli errori possono causare enormi problemi, con arresti anomali del backend o danneggiamento dei dati. I linguaggi personalizzati pl, come pl/R, pl/Ruby, pl/Perl e così via, offrono la possibilità di scrivere sia il database che i livelli app nelle stesse lingue. Questo può essere utile, poiché significa che non è necessario insegnare a un programmatore Perl Java o pl/pgsql ecc. Per scrivere un UDF.

Infine, esiste la lingua pl/proxy . Questo linguaggio UDF ti consente di eseguire la tua applicazione su dozzine o più server postgresql back-end a fini di ridimensionamento. È stato sviluppato dalla brava gente di Skype e sostanzialmente consente la soluzione di ridimensionamento orizzontale di un uomo povero. È sorprendentemente facile anche scrivere.

Ora, per quanto riguarda il problema delle prestazioni. Questa è una zona grigia. Stai scrivendo un'app per una persona? O per 1.000? o per 10.000.000? Il modo in cui costruisci la tua app e usi gli UDF dipenderà MOLTO da come stai cercando di ridimensionare. Se stai scrivendo per migliaia e migliaia di utenti, la cosa principale che vuoi fare è ridurre il carico sul db il più possibile. Gli UDF che riducono la quantità di dati spostati all'esterno e nel database contribuiranno a ridurre IO. Tuttavia, se iniziano ad aumentare il carico della CPU, potrebbero essere un problema allora. In generale, ridurre IO il carico è la prima priorità e assicurarsi che gli UDF siano efficienti in modo da non sovraccaricare la CPU successiva.

7
Scott Marlowe