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

Una guida a Pgpool per PostgreSQL:prima parte

Pgpool è meno attuale oggi, rispetto a 10 anni fa, quando era la parte predefinita di una configurazione PostgreSQL di produzione. Spesso quando qualcuno parlava del cluster PostgreSQL, si riferiva a postgreSQL dietro pgpool e non all'istanza PostgreSQL stessa (che è il termine giusto). Pgpool è riconosciuto tra i giocatori Postgres più influenti:comunità postgresql, commandprompt, 2ndquadrant, EDB, citusdata, postgrespro (ordinato per età, non influenza). Mi rendo conto che il livello di riconoscimento nei miei link è molto diverso:voglio solo enfatizzare l'impatto generale di pgpool nel mondo postgres. Alcuni degli attuali "venditori" postgres più conosciuti sono stati trovati dopo che il pgpool era già famoso. Allora, cosa lo rende così famoso?

Solo l'elenco delle funzionalità offerte più richieste lo rende fantastico:

  • replica nativa
  • pooling di connessioni
  • bilanciamento del carico per la scalabilità in lettura
  • elevata disponibilità (watchdog con IP virtuale, ripristino online e failover)

Bene, creiamo una sandbox e giochiamo. La mia configurazione di esempio è la modalità master slave. Presumo che sia il più popolare oggi, perché in genere usi la replica in streaming insieme al bilanciamento del carico. La modalità di replica è usata a malapena in questi giorni. La maggior parte dei DBA lo salta a favore della replica in streaming e pglogical e in precedenza a slony.

La modalità di replica ha molte impostazioni interessanti e funzionalità sicuramente interessanti. Ma la maggior parte dei DBA ha una configurazione master/multi slave quando arriva a pgpool. Quindi sono alla ricerca di failover automatico e bilanciamento del carico e pgpool lo offre pronto all'uso per gli ambienti master/multi slave esistenti. Per non parlare del fatto che a partire da Postgres 9.4, la replica in streaming funziona senza bug importanti e da 10 indici hash è supportata la replica, quindi non c'è quasi nulla che ti impedisca di usarlo. Anche la replica in streaming è asincrona per impostazione predefinita (configurabile per configurazioni complicate di sincronizzazione sincrona e anche non "lineare", mentre la replica nativa di pgpool è sincrona (il che significa modifiche dei dati più lente) senza alcuna opzione di scelta. Inoltre si applicano limitazioni aggiuntive. Il manuale stesso di Pgpool suggerisce di preferire quando possibile la replica in streaming su quella nativa di pgpool). E quindi questa è la mia scelta qui.

Ah, ma prima dobbiamo installarlo, giusto?

Installazione (di versione superiore su Ubuntu).

Per prima cosa controllando la versione di Ubuntu con lsb_release -a. Per me repo è:

[email protected]:~# sudo add-apt-repository 'deb http://apt.postgresql.org/pub/repos/apt/ xenial-pgdg main'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
>   sudo apt-key add -
OK
[email protected]:~# sudo apt-get update

Infine l'installazione stessa:

sudo apt-get install pgpool2=3.7.2-1.pgdg16.04+1

Configurazione:

I user default config dalla modalità consigliata:

zcat /usr/share/doc/pgpool2/examples/pgpool.conf.sample-stream.gz > /etc/pgpool2/pgpool.conf

A partire:

Se hai perso la configurazione, vedrai:

2018-03-22 13:52:53.284 GMT [13866] FATAL:  role "nobody" does not exist

Ah vero - mio cattivo, ma facilmente risolvibile (fattibile alla cieca con una fodera se vuoi lo stesso utente per tutti i controlli sanitari e il ripristino):

[email protected]:~# sed -i s/'nobody'/'pgpool'/g /etc/pgpool2/pgpool.conf

E prima di andare oltre, creiamo il database pgpool e l'utente pgpool in tutti i cluster (nella mia sandbox sono master, failover e slave, quindi devo eseguirlo solo su master):

t=# create database pgpool;
CREATE DATABASE
t=# create user pgpool;
CREATE ROLE

Finalmente - a partire:

[email protected]:~$ /usr/sbin/service pgpool2 start
[email protected]:~$ /usr/sbin/service pgpool2 status
pgpool2.service - pgpool-II
   Loaded: loaded (/lib/systemd/system/pgpool2.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2018-04-09 10:25:16 IST; 4h 14min ago
     Docs: man:pgpool(8)
  Process: 19231 ExecReload=/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
 Main PID: 8770 (pgpool)
    Tasks: 10
   Memory: 5.5M
      CPU: 18.250s
   CGroup: /system.slice/pgpool2.service
           ├─ 7658 pgpool: wait for connection reques
           ├─ 7659 pgpool: wait for connection reques
           ├─ 7660 pgpool: wait for connection reques
           ├─ 8770 /usr/sbin/pgpool -n
           ├─ 8887 pgpool: PCP: wait for connection reques
           ├─ 8889 pgpool: health check process(0
           ├─ 8890 pgpool: health check process(1
           ├─ 8891 pgpool: health check process(2
           ├─19915 pgpool: postgres t ::1(58766) idl
           └─23730 pgpool: worker proces

Ottimo, quindi possiamo procedere alla prima funzionalità:controlliamo il bilanciamento del carico. Ha alcuni requisiti da utilizzare, supporta suggerimenti (ad esempio per bilanciare nella stessa sessione), ha funzioni nella lista nera e bianca, ha un elenco di preferenze di reindirizzamento basato su espressioni regolari. È sofisticato. Purtroppo esaminare a fondo tutte quelle funzionalità non sarebbe nell'ambito di questo blog, quindi controlleremo le demo più semplici:

Innanzitutto, qualcosa di molto semplice mostrerà quale nodo viene utilizzato per selezionare (nella mia configurazione, il master gira su 5400, slave su 5402 e failover su 5401, mentre pgpool stesso è su 5433, poiché ho un altro cluster in esecuzione e non volevo interferire con esso):

[email protected]:~$ psql -h localhost -p 5433 t -c "select current_setting('port') from ts limit 1"
 current_setting
-----------------
 5400
(1 row)

Poi in loop:

[email protected]:~$ (for i in $(seq 1 99); do psql -h localhost -p 5433 t -c "select current_setting('port') from ts limit 1" -XAt; done) | sort| uniq -c
      9 5400
     30 5401
     60 5402

Grande. Bilancia decisamente il carico tra i nodi, ma sembra non bilanciare allo stesso modo:forse è così intelligente da conoscere il peso di ogni affermazione? Verifichiamo la distribuzione con i risultati attesi:

t=# show pool_nodes;
 node_id | hostname  | port | status | lb_weight |  role   | select_cnt | load_balance_node | replication_delay
---------+-----------+------+--------+-----------+---------+------------+-------------------+-------------------
 0       | localhost | 5400 | up     | 0.125000  | primary | 122        | false             | 0
 1       | localhost | 5401 | up     | 0.312500  | standby | 169        | false             | 0
 2       | localhost | 5402 | up     | 0.562500  | standby | 299        | true              | 0
(3 rows)

No - pgpool non analizza il peso delle affermazioni - era di nuovo un DBA con le sue impostazioni! Le impostazioni (vedere l'attributo lb_weight) si riconciliano con gli effettivi obiettivi di destinazione della query. Puoi facilmente modificarlo (come abbiamo fatto qui) modificando l'impostazione corrispondente, ad esempio:

[email protected]:~$ grep weight /etc/pgpool2/pgpool.conf
backend_weight0 =0.2
backend_weight1 = 0.5
backend_weight2 = 0.9
[email protected]:~# sed -i s/'backend_weight2 = 0.9'/'backend_weight2 = 0.2'/ /etc/pgpool2/pgpool.conf
[email protected]:~# grep backend_weight2 /etc/pgpool2/pgpool.conf
backend_weight2 = 0.2
[email protected]:~# pgpool reload
[email protected]:~$ (for i in $(seq 1 9); do psql -h localhost -p 5433 t -c "select current_setting('port') from ts limit 1" -XAt; done) | sort| uniq -c
      6 5401
      3 5402
Scarica il whitepaper oggi Gestione e automazione di PostgreSQL con ClusterControlScopri ciò che devi sapere per distribuire, monitorare, gestire e ridimensionare PostgreSQLScarica il whitepaper

Grande! La prossima grande funzionalità offerta è il pool di connessioni. Con 3.5 il "problema della mandria fulminea" viene risolto serializzando le chiamate accept(), accelerando notevolmente i tempi di "connessione client". Eppure questa funzione è piuttosto semplice. Non offre diversi livelli di pooling o diversi pool configurati per lo stesso database (pgpool ti consente di scegliere dove eseguire select con database_redirect_preference_list di bilanciamento del carico però) o altre funzionalità flessibili offerte da pgBouncer.

Così breve demo:

t=# select pid,usename,backend_type, state, left(query,33) from pg_stat_activity where usename='vao' and pid <> pg_backend_pid();
 pid  | usename |  backend_type  | state |     left
------+---------+----------------+-------+--------------
 8911 | vao     | client backend | idle  |  DISCARD ALL
 8901 | vao     | client backend | idle  |  DISCARD ALL
 7828 | vao     | client backend | idle  |  DISCARD ALL
 8966 | vao     | client backend | idle  |  DISCARD ALL
(4 rows)
Hm - did I set up this little number of children?
t=# pgpool show num_init_children;
 num_init_children
-------------------
 4
(1 row)

Ah, vero, li ho cambiati a un valore inferiore a 32 predefinito, quindi l'output non richiederebbe diverse pagine. Bene, allora, proviamo a superare il numero di sessioni (sotto apro sessioni postgres async in loop, quindi le 6 sessioni sarebbero richieste più o meno contemporaneamente):

[email protected]:~$ for i in $(seq 1 6); do (psql -h localhost -p 5433 t -U vao -c "select pg_backend_pid(), pg_sleep(1), current_setting('port'), clock_timestamp()" &);  done
[email protected]:~$  pg_backend_pid | pg_sleep | current_setting |        clock_timestamp
----------------+----------+-----------------+-------------------------------
           8904 |          | 5402            | 2018-04-10 12:46:55.626206+01
(1 row)

 pg_backend_pid | pg_sleep | current_setting |        clock_timestamp
----------------+----------+-----------------+-------------------------------
           9391 |          | 5401            | 2018-04-10 12:46:55.630175+01
(1 row)

 pg_backend_pid | pg_sleep | current_setting |       clock_timestamp
----------------+----------+-----------------+------------------------------
           8911 |          | 5400            | 2018-04-10 12:46:55.64933+01
(1 row)

 pg_backend_pid | pg_sleep | current_setting |        clock_timestamp
----------------+----------+-----------------+-------------------------------
           8904 |          | 5402            | 2018-04-10 12:46:56.629555+01
(1 row)

 pg_backend_pid | pg_sleep | current_setting |        clock_timestamp
----------------+----------+-----------------+-------------------------------
           9392 |          | 5402            | 2018-04-10 12:46:56.633092+01
(1 row)

 pg_backend_pid | pg_sleep | current_setting |       clock_timestamp
----------------+----------+-----------------+------------------------------
           8910 |          | 5402            | 2018-04-10 12:46:56.65543+01
(1 row)

Consente alle sessioni di arrivare a tre - previsto, poiché una viene presa dalla sessione sopra (selezionando da pg_stat_activity) quindi 4-1=3. Non appena pg_sleep finisce il suo secondo pisolino e la sessione viene chiusa da postgres, viene fatta entrare la successiva. Quindi, dopo che i primi tre finiscono, i successivi tre entrano. Cosa succede al resto? Sono in coda fino a quando non si libera lo slot di connessione successivo. Quindi si verifica il processo descritto accanto a serialize_accept e il client viene connesso.

Eh? Solo pool di sessioni in modalità sessione? È tutto?.. No, qui interviene la memorizzazione nella cache! Guarda.:

postgres=# /*NO LOAD BALANCE*/ select 1;
 ?column?
----------
        1
(1 row)

Verifica della pg_stat_activity:

postgres=# select pid, datname, state, left(query,33),state_change::time(0), now()::time(0) from pg_stat_activity where usename='vao' and query not like '%DISCARD%';
  pid  | datname  | state |               left                | state_change |   now
-------+----------+-------+-----------------------------------+--------------+----------
 15506 | postgres | idle  | /*NO LOAD BALANCE*/ select 1, now | 13:35:44     | 13:37:19
(1 row)

Quindi esegui di nuovo la prima istruzione e osserva che state_change non cambia, il che significa che non arrivi nemmeno al database per ottenere un risultato noto! Ovviamente se inserisci una funzione mutabile, i risultati non verranno memorizzati nella cache. Sperimenta con:

postgres=# /*NO LOAD BALANCE*/ select 1, now();
 ?column? |             now
----------+------------------------------
        1 | 2018-04-10 13:35:44.41823+01
(1 row)

Scoprirai che state_change cambia così come il risultato.

Ultimo punto qui:perché /*NO LOAD BALANCE*/ ?... per essere sicuri controlliamo pg_stat_activity su master ed eseguiamo query anche su master. Lo stesso puoi usare /*NO QUERY CACHE*/ suggerimento per evitare di ottenere un risultato memorizzato nella cache.

Già molto per una breve recensione? Ma non abbiamo nemmeno toccato la parte HA! E molti utenti guardano a pgpool specificamente per questa funzione. Bene, questa non è la fine della storia, questa è la fine della prima parte. È in arrivo la seconda parte, in cui tratteremo brevemente HA e alcuni altri suggerimenti sull'utilizzo di pgpool...