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

Miglioramento delle prestazioni per PostgreSQL con HAProxy

Le prestazioni del database sono una preoccupazione molto importante quando si mantiene il cluster di database, soprattutto perché cresce nel tempo. Ciò è particolarmente vero se l'applicazione è iniziata con un traffico ridotto per poi crescere fino a carichi di lavoro di lettura-scrittura moderati o pesanti.

La cosa da ricordare è che non esiste una configurazione perfetta su cui poter fare affidamento a lungo, poiché alcuni carichi di lavoro potrebbero cambiare nel tempo.

Con ClusterControl, la creazione o la distribuzione di un nuovo cluster di database PostgreSQL esegue un'analisi di base come il controllo delle risorse hardware, quindi applica l'autotuning e imposta i valori per i parametri sintonizzabili selezionati. Con l'evoluzione di PostgreSQL, sono stati sviluppati anche molti strumenti per supportare diverse configurazioni, in particolare per il bilanciamento del carico.

In questo blog, daremo un'occhiata all'importanza di HAProxy e come può aiutare a migliorare le prestazioni. È un vecchio strumento, ma un potente proxy e/o bilanciamento del carico che supporta non solo i server di database, ma anche i protocolli specifici dell'applicazione di rete. HAProxy può operare rispettivamente tramite il livello quattro e il livello sette, a seconda del tipo di configurazione in base alla configurazione.

Ottimizzazione delle prestazioni PostgreSQL

Uno dei fattori principali per migliorare le prestazioni di PostgreSQL inizia con l'ottimizzazione dei parametri di base da initdb ai valori dei parametri di runtime. Questo deve essere in grado di gestire il carico di lavoro desiderato in base a determinati requisiti. Prima di poter intraprendere un percorso per la funzione HAProxy per PostgreSQL, il server del database deve essere stabile e ottimizzato per le variabili desiderate. Prendiamo un elenco di aree per PostgreSQL su quali sono le cose che possono influire sulle prestazioni del tuo server di database.

Ottimizzazione per una gestione fattibile della memoria

PostgreSQL è efficiente ed è possibile funzionare efficacemente con un minimo di 256 Mb di memoria. La memoria non è costosa, tuttavia la maggior parte dei set di dati è inferiore a 4Gib. Se hai almeno 4Gib, il tuo set di dati attivo può rimanere in file e/o cache shared_buffer.

Ottimizzare PostgreSQL per la gestione della memoria è una delle cose più importanti e basilari che devi impostare. L'impostazione appropriata può influire sull'aumento delle prestazioni del server di database. Anche se dipende dal tipo di tavoli con cui stai giocando. Anche query errate e definizioni di tabella scadenti possono portare a prestazioni scadenti. Con gli indici appropriati definiti per le tabelle e con le query che fanno riferimento agli indici, le possibilità possono raggiungere dall'80% al 100% delle query possono essere recuperate dalla memoria. Questo soprattutto se il buffer dell'indice ha il valore giusto per caricare il tuo indice definito sulle tue tabelle. Diamo un'occhiata ai parametri comunemente impostati per il miglioramento delle prestazioni.

  • buffer_condivisi - PostgreSQL ridimensiona il suo spazio di memoria principale con shared_buffers. La cache di lavoro di tutte le hot tuple (e le voci di indice) all'interno di PostgreSQL. Questo parametro imposta la quantità di memoria utilizzata dal server di database per i buffer di memoria condivisa. È una cache preallocata (buffer). Per i sistemi basati su Linux, è ideale impostare il parametro kernel kernel.shmmax che può essere impostato in modo persistente tramite il file di configurazione del kernel /etc/sysctl.conf.
  • temp_buffers - Imposta il numero massimo di buffer temporanei utilizzati per ciascuna sessione. Questi sono buffer di sessione locali utilizzati solo per accedere alle tabelle temporanee. Una sessione assegnerà i buffer temporanei secondo necessità fino al limite dato da temp_buffers.
  • work_mem - La memoria di lavoro disponibile per le operazioni di lavoro (ordinamento) prima dello scambio di PostgreSQL. Non impostare a livello globale (postgresql.conf). Usa per transazione in quanto può essere errato per query, connessione o ordinamento. Si consiglia di utilizzare EXPLAIN ANALYZE per vedere se stai traboccando o meno.
  • maintenance_work_mem - Specifica la quantità di memoria da utilizzare per le operazioni di manutenzione (VACUUM, CREATE INDEX e ALTER TABLE … ADD FOREIGN KEY…)

Ottimizzazione per una gestione fattibile del disco

Un certo numero di parametri di runtime da impostare qui. Elenchiamo cosa sono questi:

  • temp_file_limit - Specifica la quantità massima di spazio su disco che una sessione può utilizzare per i file temporanei, come l'ordinamento e l'hash dei file temporanei, o il file di archiviazione per un cursore trattenuto. Una transazione che tenta di superare questo limite verrà annullata.
  • fsync - Se fsync è abilitato, PostgreSQL cercherà di assicurarsi che gli aggiornamenti siano scritti fisicamente sul disco. Ciò garantisce che il cluster di database possa essere ripristinato in uno stato coerente dopo un arresto anomalo del sistema operativo o dell'hardware. Sebbene la disabilitazione di fsync in genere migliori le prestazioni, può causare la perdita di dati in caso di interruzione di corrente o arresto anomalo del sistema. Pertanto, è consigliabile disattivare fsync solo se puoi ricreare facilmente l'intero database da dati esterni
  • synchronous_commit - Utilizzato per imporre che il commit attenda la scrittura di WAL su disco prima di restituire uno stato di esito positivo al client. Questa variabile ha dei compromessi tra prestazioni e affidabilità. Se hai bisogno di maggiori prestazioni, impostalo su off, il che significa che quando il server si arresta in modo anomalo, tendenza a subire una perdita di dati. In caso contrario, se l'affidabilità è importante, attivarlo. Ciò significa che ci sarà un intervallo di tempo tra lo stato di successo e una scrittura su disco garantita, quindi può influire sulle prestazioni.
  • checkpoint_timeout, checkpoint_completion_target - PostgreSQL scrive le modifiche in WAL, che è un'operazione costosa. Se scrive frequentemente modifiche in WAL, può influire negativamente sulle prestazioni. Quindi, come funziona, il processo del checkpoint scarica i dati nei file di dati. Questa attività viene eseguita quando si verifica CHECKPOINT e può causare un'enorme quantità di IO. L'intero processo comporta costose operazioni di lettura/scrittura del disco. Anche se tu (utente amministratore) puoi sempre emettere CHECKPOINT ogni volta che sembra necessario o automatizzarlo impostando i valori desiderati per questi parametri. Il parametro checkpoint_timeout viene utilizzato per impostare il tempo tra i checkpoint WAL. L'impostazione di un valore troppo basso riduce il tempo di ripristino dell'arresto anomalo, poiché più dati vengono scritti sul disco, ma danneggia anche le prestazioni poiché ogni checkpoint finisce per consumare preziose risorse di sistema. Il checkpoint_completion_target è la frazione di tempo tra i checkpoint per il completamento del checkpoint. Un'elevata frequenza di checkpoint può influire sulle prestazioni. Per un checkpoint fluido, checkpoint_timeout deve essere un valore basso. In caso contrario, il sistema operativo accumulerà tutte le pagine sporche fino a quando il rapporto non viene soddisfatto e quindi procederà a un grande svuotamento.

Regolazione di altri parametri per le prestazioni

Ci sono alcuni parametri che forniscono boost e guidano le prestazioni in PostgreSQL. Elenchiamo di seguito quali sono:

  • wal_buffers - PostgreSQL scrive il suo record WAL (write ahead log) nei buffer e quindi questi buffer vengono scaricati su disco. La dimensione predefinita del buffer, definita da wal_buffers, è 16 MB, ma se hai molte connessioni simultanee, un valore più alto può fornire prestazioni migliori.
  • efficace_cache_size - La dimensione_cache_effettiva fornisce una stima della memoria disponibile per la memorizzazione nella cache del disco. È solo una linea guida, non l'esatta memoria allocata o la dimensione della cache. Non alloca memoria effettiva ma indica all'ottimizzatore la quantità di cache disponibile nel kernel. Se il valore di questo è impostato su un valore troppo basso, il pianificatore di query può decidere di non utilizzare alcuni indici, anche se sarebbero utili. Pertanto, impostare un valore elevato è sempre vantaggioso.
  • default_statistics_target - PostgreSQL raccoglie le statistiche da ciascuna delle tabelle nel suo database per decidere come verranno eseguite le query su di esse. Per impostazione predefinita, non raccoglie troppe informazioni e, se non stai ottenendo buoni piani di esecuzione, dovresti aumentare questo valore e quindi eseguire nuovamente ANALYZE nel database (o attendere l'AUTOVACUUM).

Efficienza delle query PostgreSQL 

PostgreSQL ha una funzione molto potente per ottimizzare le query. Con l'ottimizzatore di query genetiche integrato (noto come GEQO). Utilizza un algoritmo genetico che è un metodo di ottimizzazione euristica attraverso la ricerca randomizzata. Questo viene applicato quando si esegue l'ottimizzazione utilizzando JOIN che fornisce un'ottimizzazione delle prestazioni molto buona. Ciascun candidato nel piano di unione è rappresentato da una sequenza in cui unire le relazioni di base. Esegue in modo casuale una relazione genetica generando semplicemente una possibile sequenza di join ma in modo casuale.

Per ogni sequenza di join considerata, viene richiamato il codice pianificatore standard per stimare il costo dell'esecuzione della query utilizzando quella sequenza di join. Quindi, per ciascuna delle sequenze JOIN, tutte hanno i loro piani di scansione delle relazioni inizialmente determinati. Quindi, il piano di query calcolerà il piano più fattibile e performante, ovvero con un costo stimato inferiore e sarà considerato "più idoneo" rispetto a quelli con un costo maggiore.

Dato che ha una potente funzionalità integrata in PostgreSQL e i parametri configurati appropriati in base ai requisiti desiderati, non vanifica la fattibilità quando si tratta di prestazioni se il carico viene lanciato solo su un nodo primario. Il bilanciamento del carico con HAProxy aiuta a migliorare ulteriormente le prestazioni di PostgreSQL.

Guidare le prestazioni per PostgreSQL con la suddivisione in lettura e scrittura 

Potresti avere ottime prestazioni con il tuo nodo del server PostgreSQL, ma potresti non essere in grado di anticipare il tipo di carico di lavoro che potresti avere, specialmente quando il traffico è elevato e la domanda va oltre i limiti. Il bilanciamento del carico tra primario e secondario fornisce un aumento delle prestazioni all'interno dell'applicazione e/o dei client che si connettono al cluster di database PostgreSQL. Come farlo, non è più una domanda in quanto è una configurazione molto comune per un'elevata disponibilità e ridondanza quando si tratta di distribuire il carico ed evitare che il nodo primario si impantani a causa dell'elaborazione del carico elevato.

La configurazione con HAProxy è facile. Tuttavia, è più efficiente, più veloce e fattibile con ClusterControl. Quindi useremo ClusterControl per configurarlo per noi.

Configurazione di PostgreSQL con HAProxy

Per fare ciò, dovremo semplicemente installare e configurare HAProxy sopra i cluster PostgreSQL. HAProxy ha una funzione per supportare PostgreSQL tramite l'opzione pgsql-check, ma il suo supporto è un'implementazione molto semplice per determinare se un nodo è attivo o meno. Non ha controlli per identificare un nodo primario e un nodo di ripristino. Un'opzione è usare xinetd per il quale faremo affidamento sulla comunicazione di HAProxy per l'ascolto tramite il nostro servizio xinetd che controlla lo stato di un particolare nodo nel nostro cluster PostgreSQL.

In ClusterControl, vai a Gestisci → Bilanciatore del carico proprio come di seguito,

Quindi segui semplicemente in base all'interfaccia utente per lo screenshot qui sotto. È possibile fare clic su Mostra impostazioni avanzate per visualizzare opzioni più avanzate. Tuttavia, seguire l'interfaccia utente è molto semplice. Vedi sotto,

Sto importando solo HAProxy a nodo singolo senza ridondanza ma allo scopo di questo blog, rendiamolo più semplice.

Il mio esempio di visualizzazione HAProxy è mostrato di seguito,

Come mostrato sopra, 192.168.30.20 e 192.168.30.30 sono i principali e nodi secondari/di ripristino rispettivamente. Considerando che HAProxy è installato nel nodo secondario/di ripristino. Idealmente, potresti installare HAProxy su più nodi per avere più ridondanza e alta disponibilità, è meglio isolarlo rispetto ai nodi del database. Se hai un budget limitato o stai risparmiando sul tuo utilizzo, potresti scegliere di installare i tuoi nodi HAProxy, dove sono installati anche i nodi del tuo database.

ClusterControl lo imposta automaticamente e include anche il servizio xinetd per il controllo PostgreSQL. Questo può essere verificato con netstat proprio come di seguito,

[email protected]:~# netstat -tlv4np|grep haproxy

tcp        0      0 0.0.0.0:5433            0.0.0.0:*               LISTEN      28441/haproxy

tcp        0      0 0.0.0.0:5434            0.0.0.0:*               LISTEN      28441/haproxy

tcp        0      0 0.0.0.0:9600            0.0.0.0:*               LISTEN      28441/haproxy

Mentre la porta 5433 è di lettura-scrittura e 5444 è di sola lettura.

Per PostgreSQL, controlla il servizio xinetd, ovvero postgreshk, come mostrato di seguito,

[email protected]:~# cat /etc/xinetd.d/postgreschk

# default: on

# description: postgreschk

service postgreschk

{

        flags           = REUSE

        socket_type     = stream

        port            = 9201

        wait            = no

        user            = root

        server          = /usr/local/sbin/postgreschk

        log_on_failure  += USERID

        disable         = no

        #only_from       = 0.0.0.0/0

        only_from       = 0.0.0.0/0

        per_source      = UNLIMITED

}

Anche i servizi xinetd si basano su /etc/services, quindi potresti essere in grado di trovare la porta designata per la mappatura.

[email protected]:~# grep postgreschk /etc/services

postgreschk        9201/tcp

Se hai bisogno di cambiare la porta del tuo postgreschk su quale porta mappare, devi cambiare questo file oltre al file di configurazione del servizio e poi non dimenticare di riavviare il demone xinetd.

Il servizio postgreschk contiene un riferimento a un file esterno che fondamentalmente fa un controllo per i nodi se è scrivibile, il che significa che è un primario o un master. Se un nodo è in fase di ripristino, è una replica o un nodo di ripristino.

[email protected]:~# cat /usr/local/sbin/postgreschk

#!/bin/bash

#

# This script checks if a PostgreSQL server is healthy running on localhost. It will

# return:

# "HTTP/1.x 200 OK\r" (if postgres is running smoothly)

# - OR -

# "HTTP/1.x 500 Internal Server Error\r" (else)

#

# The purpose of this script is make haproxy capable of monitoring PostgreSQL properly

#



export PGHOST='localhost'

export PGUSER='s9smysqlchk'

export PGPASSWORD='password'

export PGPORT='7653'

export PGDATABASE='postgres'

export PGCONNECT_TIMEOUT=10



FORCE_FAIL="/dev/shm/proxyoff"



SLAVE_CHECK="SELECT pg_is_in_recovery()"

WRITABLE_CHECK="SHOW transaction_read_only"



return_ok()

{

    echo -e "HTTP/1.1 200 OK\r\n"

    echo -e "Content-Type: text/html\r\n"

    if [ "$1x" == "masterx" ]; then

        echo -e "Content-Length: 56\r\n"

        echo -e "\r\n"

        echo -e "<html><body>PostgreSQL master is running.</body></html>\r\n"

    elif [ "$1x" == "slavex" ]; then

        echo -e "Content-Length: 55\r\n"

        echo -e "\r\n"

        echo -e "<html><body>PostgreSQL slave is running.</body></html>\r\n"

    else

        echo -e "Content-Length: 49\r\n"

        echo -e "\r\n"

        echo -e "<html><body>PostgreSQL is running.</body></html>\r\n"

    fi

    echo -e "\r\n"



    unset PGUSER

    unset PGPASSWORD

    exit 0

}



return_fail()

{

    echo -e "HTTP/1.1 503 Service Unavailable\r\n"

    echo -e "Content-Type: text/html\r\n"

    echo -e "Content-Length: 48\r\n"

    echo -e "\r\n"

    echo -e "<html><body>PostgreSQL is *down*.</body></html>\r\n"

    echo -e "\r\n"



    unset PGUSER

    unset PGPASSWORD

    exit 1

}



if [ -f "$FORCE_FAIL" ]; then

    return_fail;

fi



# check if in recovery mode (that means it is a 'slave')

SLAVE=$(psql -qt -c "$SLAVE_CHECK" 2>/dev/null)

if [ $? -ne 0 ]; then

    return_fail;

elif echo $SLAVE | egrep -i "(t|true|on|1)" 2>/dev/null >/dev/null; then

    return_ok "slave"

fi



# check if writable (then we consider it as a 'master')

READONLY=$(psql -qt -c "$WRITABLE_CHECK" 2>/dev/null)

if [ $? -ne 0 ]; then

    return_fail;

elif echo $READONLY | egrep -i "(f|false|off|0)" 2>/dev/null >/dev/null; then

    return_ok "master"

fi



return_ok "none";

La combinazione utente/password deve essere un RUOLO valido nel tuo server PostgreSQL. Poiché stiamo installando tramite ClusterControl, questo viene gestito automaticamente.

Ora che abbiamo un'installazione completa di HAProxy, questa configurazione ci consente di avere una suddivisione in lettura-scrittura in cui le scritture di lettura vanno al nodo primario o scrivibile, mentre in sola lettura sia per il primario che per il secondario/ nodi di ripristino. Questa configurazione non significa che sia già performante, è stata comunque ottimizzata come discusso in precedenza con una combinazione di HAProxy per il bilanciamento del carico che aggiunge un ulteriore aumento delle prestazioni per la tua applicazione e i rispettivi client di database.