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

Una guida all'uso di pgBouncer per PostgreSQL

Quando leggi PostgreSQL per iniziare, vedi la riga:“Il server PostgreSQL può gestire più connessioni simultanee dai client. Per ottenere ciò, avvia ("fork") un nuovo processo per ogni connessione. Da quel momento in poi, il client e il nuovo processo server comunicano senza l'intervento del processo postgres originale. Pertanto, il processo del server master è sempre in esecuzione, in attesa di connessioni client, mentre i processi client e server associati vanno e vengono.

Idea brillante. Eppure significa che ogni nuova connessione avvia un nuovo processo, riservando RAM e forse diventando troppo pesante con più sessioni. Per evitare problemi, postgres ha l'impostazione max_connections con 100 connessioni predefinite. Ovviamente puoi aumentarlo, ma tale azione richiederebbe il riavvio (pg_settings.context è 'postmaster'):

t=# select name,setting,short_desc,context from pg_settings where name = 'max_connections';
-[ RECORD 1 ]--------------------------------------------------
name       | max_connections
setting    | 100
short_desc | Sets the maximum number of concurrent connections.
context    | postmaster
Alcune letture interessanti Utilizzo di PgBouncer Qual è lo scopo di rimbalzare? Log delle modifiche di PgBouncer Post contenenti "pgbouncer" su Stack Overflow Post taggati "pgbouncer" su 2ndQuadrant

E anche dopo l'aumento, a un certo punto potresti aver bisogno di più connessioni (ovviamente urgentemente come sempre durante l'esecuzione di prod). Perché aumentarlo è così scomodo? Perché se fosse comodo, probabilmente finiresti con un aumento spontaneo incontrollato del numero fino a quando il cluster non inizia a ritardare. Ciò significa che le vecchie connessioni sono più lente, quindi richiedono più tempo, quindi sono necessarie sempre più nuove. Per evitare tale possibile valanga e aggiungere una certa flessibilità, abbiamo superuser_reserved_connections - per essere in grado di connettersi e risolvere i problemi con SU quando max_connections è esaurito. E ovviamente vediamo la necessità di un pool di connessioni. Poiché vogliamo che i nuovi candidati alla connessione aspettino in coda invece di fallire con l'eccezione FATAL:scusa, troppi clienti già e non rischiare il postmaster.

Il pool di connessioni è offerto a un certo livello da molti "client" popolari. Potresti usarlo con jdbc per un bel po'. Recentemente node-postgres ha offerto il proprio node-pg-pool. Più o meno l'implementazione è semplice (come lo è l'idea):pooler avvia le connessioni verso il database e le mantiene. Il client che si connette a db ottiene solo una connessione esistente "condivisa" e dopo averla chiusa, la connessione torna al pool. Abbiamo anche software molto più sofisticati, come pgPool. Eppure pgbouncer è una scelta estremamente popolare per l'attività. Come mai? Perché fa solo la parte di pooling, ma lo fa bene. È gratis. È abbastanza semplice da configurare. E lo incontri nella maggior parte dei più grandi fornitori di servizi come consigliato o utilizzato, ad esempio citusdata, aws, heroku e altre risorse altamente rispettate.

Quindi diamo un'occhiata più da vicino a cosa può e come lo usi. Nella mia configurazione utilizzo default pool_mode =transaction (sezione [pgbouncer]) che è una scelta molto popolare. In questo modo non solo accodiamo le connessioni che superano max_connections, ma riutilizziamo le sessioni senza attendere la chiusura della connessione precedente:

[databases]
mon = host=1.1.1.1 port=5432 dbname=mon
mons = host=1.1.1.1 port=5432 dbname=mon pool_mode = session pool_size=2 max_db_connections=2
monst = host=1.1.1.1 port=5432 dbname=mon pool_mode = statement
[pgbouncer]
listen_addr = 1.1.1.1
listen_port = 6432
unix_socket_dir = /tmp
auth_file = /pg/pgbouncer/bnc_users.txt
auth_type = hba
auth_hba_file = /pg/pgbouncer/bnc_hba.conf
admin_users = root vao
pool_mode = transaction
server_reset_query = RESET ALL; --DEALLOCATE ALL; /* custom */
ignore_startup_parameters = extra_float_digits
application_name_add_host = 1
max_client_conn = 10000
autodb_idle_timeout = 3600
default_pool_size = 100
max_db_connections = 100
max_user_connections = 100
#server_reset_query_always = 1 #uncomment if you want older global behaviour

Breve panoramica delle impostazioni più popolari e suggerimenti e trucchi:

  • server_reset_query è molto utile e importante. In modalità pool di sessioni, "cancella" gli "artefatti" della sessione precedente. Altrimenti avresti problemi con gli stessi nomi per le istruzioni preparate, le impostazioni di sessione che interessano le sessioni successive e così via. L'impostazione predefinita è SCARTA TUTTO, che "reimposta" tutti gli stati della sessione. Tuttavia puoi scegliere valori più sofisticati, ad esempio RESET ALL; DEALLOCA TUTTO; dimenticare solo SET SESSION e rendiconti preparati, mantenendo “condivisi” tabelle TEMP e piani. O il contrario:potresti voler fare affermazioni preparate "globali" da qualsiasi sessione. Tale configurazione è fattibile, anche se rischiosa. Devi fare in modo che pgbouncer riutilizzi la sessione per tutti (rendendo così dimensioni molto ridotte della piscina o mandando a valanga le sessioni), il che non è completamente affidabile. Comunque - è un'abilità utile. Soprattutto nelle configurazioni in cui si desidera che le sessioni client alla fine (non immediatamente) cambino alle impostazioni della sessione in pool configurate. Un punto molto importante qui è la modalità pool di sessioni. Prima della 1.6 questa impostazione interessava anche altre modalità del pool, quindi se ci si basava su di essa, è necessario utilizzare la nuova impostazione server_reset_query_always =1. Probabilmente a un certo punto le persone vorranno che server_reset_query sia ancora più flessibile e configurabile per coppia db/utente ( e client_reset_query invece). Ma al momento della stesura di questo articolo, marzo 2018, non è un'opzione. L'idea alla base di rendere questa impostazione valida per impostazione predefinita solo per la modalità sessione era, se condividi la connessione a livello di transazione o di rendiconto, non puoi assolutamente fare affidamento sull'impostazione della sessione.

  • Auth_type =hba. Prima della 1.7, il grosso problema con pgbouncer era l'assenza dell'autenticazione basata sull'host - "firewall postgres". Ovviamente lo avevi ancora per la connessione al cluster postgres, ma pgbouncer era "aperto" per qualsiasi fonte. Ora possiamo utilizzare lo stesso hba.conf per limitare le connessioni per host/db/utente in base alla rete di connessione.

  • connect_query non viene eseguito su ogni "connessione" client a pgbouncer, ma piuttosto quando pgbouncer si connette a un'istanza Postgres. Quindi non puoi usarlo per impostare o sovrascrivere le impostazioni "predefinite". In modalità sessione, le altre sessioni non si influenzano a vicenda e alla disconnessione, la query di ripristino elimina tutto, quindi non è necessario pasticciare. Nella modalità di pooling delle transazioni, speri di usarlo per sovrascrivere le impostazioni impostate erroneamente da altre sessioni, ma purtroppo non funzionerà. Per esempio. vuoi condividere la dichiarazione preparata tra le "sessioni" in modalità transazione, quindi imposti qualcosa come

    trns = dbname=mon pool_mode = transaction connect_query = 'do $$ begin raise warning $w$%$w$, $b$new connection$b$; end; $$; prepare s(int) as select $1;'

    e infatti - ogni nuovo client vede le istruzioni preparate (a meno che tu non abbia lasciato server_reset_query_always su on, quindi pgbouncer lo scarta al commit). Ma se qualche client esegue DISCARD s; nella sua sessione, influisce su tutti i client su questa connessione e i nuovi client che si connettono ad essa non vedranno più le istruzioni preparate. Ma se vuoi avere qualche impostazione iniziale per le connessioni postgres provenienti da pgbouncer, allora questo è il posto giusto.

  • application_name_add_host è stato aggiunto in 1.6, ha limitazioni simili. "Mette" l'IP del client in application_name, in modo da poter ottenere facilmente la tua fonte di query errata, ma viene facilmente sovrascritto dal semplice set application_name TO "non ero io"; Tuttavia puoi "curare" questo usando le visualizzazioni:segui questo post per farti un'idea o anche usare queste brevi istruzioni. Fondamentalmente l'idea è quella di mostrare i clienti; mostrerà l'IP del client, quindi puoi interrogarlo direttamente dal database di pgbouncer su ogni selezione da pg_stat_activity per verificare se è ripristinato. Ma ovviamente usare un'impostazione semplice è molto più semplice e accogliente. Anche se non garantisce il risultato...

  • pool_mode può essere specificato sia come predefinito, per database e per utente, il che lo rende molto flessibile. Le modalità di missaggio rendono pgbouncer estremamente efficace per il pool. Questa è una funzionalità potente, ma bisogna stare attenti quando la si usa. Spesso gli utenti lo usano senza comprendere i risultati per mix assolutamente atomici di per transazione/per sessione/per utente/per database/impostazioni globali che funzionano in modo diverso per lo stesso utente o database, a causa delle diverse modalità di pooling con pgbouncer. Questa è la scatola di fiammiferi che non dai ai bambini senza supervisione. Inoltre molte altre opzioni sono configurabili per default e per db e per utente.

  • Per favore, non prenderlo alla lettera, ma puoi "confrontare" diverse sezioni di ini con SET e ALTER:SET LOCAL influisce sulle transazioni ed è buono da usare quando poll_mode=transaction , SET SESSION influisce sulle sessioni ed è sicuro per l'uso quando poll_mode=session , ALTER USER SET influisce sui ruoli e interferirà con pgbouncer.ini parte della sezione [utenti], ALTER DATABASE SET influisce sui database e interferirà con pgbouncer.ini parte della sezione [database], ALTER SYSTEM SET o la modifica di postgres.conf influisce globalmente sui valori predefiniti ed è paragonabile a tutti gli effetti alla sezione predefinita di pgbouncer.ini.

  • Ancora una volta:usa la modalità pool in modo responsabile. Le istruzioni preparate o le impostazioni a livello di sessione saranno un pasticcio nella modalità di pooling delle transazioni. Come la transazione SQL non ha senso nella modalità di pool di istruzioni. Scegli una modalità di pooling adatta per connessioni adeguate. Una buona pratica è creare ruoli con l'idea che:

    • alcuni eseguiranno solo selezioni rapide, quindi possono condividere una sessione senza transazioni per un centinaio di selezioni simultanee minuscole non importanti.
    • Alcuni membri del ruolo sono sicuri per la concorrenza a livello di sessione e utilizzano SEMPRE le transazioni. Così possono condividere in sicurezza diverse sessioni per centinaia di transazioni simultanee.
    • Alcuni ruoli sono troppo complicati o complicati per condividere la loro sessione con gli altri. Quindi usi la modalità di pooling delle sessioni per evitare errori di connessione quando tutti gli "slot" sono già occupati.
  • Non usarlo al posto di HAProxy o qualche altro sistema di bilanciamento del carico. Nonostante pgbouncer abbia diverse funzionalità configurabili che indirizzano ciò che un sistema di bilanciamento del carico indirizza, come dns_max_ttl e puoi impostare una configurazione DNS per esso, la maggior parte degli ambienti prod utilizza HAProxy o qualche altro sistema di bilanciamento del carico per HA. Questo perché HAProxy è davvero bravo a bilanciare il carico tra server live in modalità round robin, meglio di pgbouncer. Sebbene pgbouncer sia migliore per il pool di connessioni postgres, potrebbe essere meglio usare un piccolo demone che esegue perfettamente un'attività, invece di uno più grande che esegue due attività, ma peggio.

  • Le modifiche alla configurazione possono essere complicate. Alcune modifiche a pgbouncer.ini richiedono il riavvio (listen_port e simili), mentre altre come admin_users richiedono il ricaricamento o SIGHUP. Le modifiche all'interno di auth_hba_file richiedono il ricaricamento, mentre le modifiche a auth_file no.

La panoramica estremamente breve delle impostazioni di cui sopra è limitata dal formato. Vi invito a dare un'occhiata all'elenco completo. Pgbouncer è il tipo di software con una quantità molto piccola di "impostazioni noiose":hanno tutti un potenziale enorme e sono di straordinario interesse.

Scarica il whitepaper oggi Gestione e automazione di PostgreSQL con ClusterControlScopri cosa devi sapere per distribuire, monitorare, gestire e ridimensionare PostgreSQLScarica il whitepaper

E infine, passando da una breve recensione entusiasta a qualcosa di cui potresti essere meno felice:l'installazione. Il processo è chiaramente descritto in questa sezione della documentazione. L'unica opzione descritta è la creazione da sorgenti git. Ma tutti sanno che ci sono pacchetti! Provando entrambi i più popolari:

sudo yum install pgbouncer
sudo apt-get install pgbouncer

può funzionare. Ma a volte devi fare un passo in più. Ad esempio, quando non è disponibile alcun pacchetto pgbouncer, prova questo.

O anche:

sudo yum install pgbouncer
Loaded plugins: priorities, update-motd, upgrade-helper
amzn-main                                                                                                                    | 2.1 kB  00:00:00
amzn-updates                                                                                                                 | 2.5 kB  00:00:00
docker-ce-edge                                                                                                               | 2.9 kB  00:00:00
docker-ce-stable                                                                                                             | 2.9 kB  00:00:00
docker-ce-test                                                                                                               | 2.9 kB  00:00:00
pgdg10                                                                                                                       | 4.1 kB  00:00:00
pgdg95                                                                                                                       | 4.1 kB  00:00:00
pgdg96                                                                                                                       | 4.1 kB  00:00:00
pglogical                                                                                                                    | 3.0 kB  00:00:00
sensu                                                                                                                        | 2.5 kB  00:00:00
(1/3): pgdg96/x86_64/primary_db                                                                                              | 183 kB  00:00:00
(2/3): pgdg10/primary_db                                                                                                     | 151 kB  00:00:00
(3/3): pgdg95/x86_64/primary_db                                                                                              | 204 kB  00:00:00
50 packages excluded due to repository priority protections
Resolving Dependencies
--> Running transaction check
---> Package pgbouncer.x86_64 0:1.8.1-1.rhel6 will be installed
--> Processing Dependency: libevent2 >= 2.0 for package: pgbouncer-1.8.1-1.rhel6.x86_64
--> Processing Dependency: c-ares for package: pgbouncer-1.8.1-1.rhel6.x86_64
--> Processing Dependency: libcares.so.2()(64bit) for package: pgbouncer-1.8.1-1.rhel6.x86_64
--> Running transaction check
---> Package c-ares.x86_64 0:1.13.0-1.5.amzn1 will be installed
---> Package pgbouncer.x86_64 0:1.8.1-1.rhel6 will be installed
--> Processing Dependency: libevent2 >= 2.0 for package: pgbouncer-1.8.1-1.rhel6.x86_64
--> Finished Dependency Resolution
Error: Package: pgbouncer-1.8.1-1.rhel6.x86_64 (pgdg10)
           Requires: libevent2 >= 2.0
 You could try using --skip-broken to work around the problem
 You could try running: rpm -Va --nofiles --nodigest

Ovviamente l'aggiunta di pgdg a /etc/yum.repos.d/ non aiuterà più. Né --skip-broken né rpm -Va --nofiles --nodigest. Un semplice

sudo yum install libevent2
Loaded plugins: priorities, update-motd, upgrade-helper
50 packages excluded due to repository priority protections
No package libevent2 available.
Error: Nothing to do

sarebbe troppo facile. Quindi devi costruire tu stesso libevent2, riportandoti alla posizione in cui devi compilare le cose da solo. O è pgbouncer o una delle sue dipendenze.

Ancora una volta - scavare troppo in profondità con le particolarità dell'installazione è fuori portata. Dovresti sapere che hai una grande possibilità di installarlo come pacchetto.

Infine, domande come "perché Postgres non offre un pool di sessioni nativo" vengono continuamente. Ci sono anche suggerimenti e pensieri molto freschi su di esso. Ma finora l'approccio più popolare qui è usare pgbouncer.