PRODPiattaforma BaaS europea sovranaApri Dashboard →

Prestazioni · 9 lettura minima

Postgres max_connections senza pooler

Affane Daylami · Fondateur · 6 giugno 2026

Torniamo al blog

Senza il pooling davanti al tuo server, max_connections dovrebbe coprire ogni connessione client aperta allo stesso tempo, non il numero di richieste che Postgres può elaborare in modo efficiente in parallelo. Confondere questi due numeri è la causa più comune di max_connections impostati in modo errato: troppo basso per assorbire il carico o troppo alto per la memoria effettivamente disponibile.

Questo testo inglese è stato generato automaticamente dall'originale francese e non è stato ancora rivisto.
Questa pagina è stata tradotta automaticamente. Fa fede la versione inglese.

Questo articolo fornisce la formula pubblicata dal wiki di PostgreSQL per calcolare la concorrenza ideale del tuo hardware (la formula di dimensionamento del pool di connessioni più citata nell'ecosistema), spiega perché ogni connessione costa più di un thread dell'applicazione, quindi descrive in dettaglio la procedura per impostare max_connections senza indovinare. La nostra metodologia di benchmark documenta il protocollo di misurazione utilizzato per qualsiasi affermazione sulle prestazioni su questo blog.

L'essenziale

  • Senza pooling, max_connections dovrebbe coprire tutte le connessioni client simultanee, non solo quelle che Postgres può elaborare in modo efficiente in parallelo.
  • Formula di riferimento wiki PostgreSQL: concorrenza attiva ideale = (core fisici × 2) + dischi efficienti. Un punto di partenza da convalidare mediante misurazione, non un limite rigido.
  • max_connections è un parametro di contesto postmaster: modificarlo richiede un riavvio completo del server, non un semplice ricaricamento.
  • Ogni connessione Postgres è un processo di sistema separato, non un thread leggero: questo è ciò che rende reale il sovraccarico non appena il numero di connessioni aumenta.
  • Verificato nel codice: sui suoi cluster Postgres dedicati, Aurabase varia max_connections da 50 (livello gratuito) a 400 (livello enterprise) a seconda della dimensione del cluster.
#
Diagnosi

Perché una connessione Postgres costa più di un thread dell'applicazione

Postgres non utilizza un pool di thread leggero per le sue connessioni. Ogni connessione client attiva un processo di sistema completo.

Il processo postmaster ne crea uno nuovo (“fork”) per ogni tentativo di connessione, dedicato a questa singola sessione fino alla sua chiusura. La documentazione ufficiale del progetto descrive precisamente questo meccanismo nel capitolo sui fondamenti dell'architettura (postgresql.org/docs/current/connect-estab.html, sezione "Connection Semantics", accesso il 24 agosto 2026).

Questa scelta ha un vantaggio reale: un crash su una connessione non influisce sulle altre, essendo ogni processo isolato dal resto del server. Ha anche un costo diretto: ogni connessione aggiuntiva aggiunge alla pianificazione un intero processo del sistema operativo, con il proprio spazio di memoria e il proprio sovraccarico di commutazione del contesto per il kernel.

Cosa cambia nella pratica

Un'applicazione che apre 500 connessioni dirette a Postgres senza pooling costringe il server a gestire 500 processi di sistema simultanei, anche se la stragrande maggioranza di essi rimane inattiva tra due richieste.

#
Costo della memoria

Cosa consuma effettivamente una connessione: memoria condivisa e work_mem

Due meccanismi distinti incidono sulla memoria e confonderli porta quasi sempre a una diagnosi errata.

Il primo è fisso. All'avvio Postgres riserva strutture di memoria condivisa (lock, tabella dei processi) dimensionate sul valore di max_connections, indipendentemente dal fatto che tali connessioni vengano successivamente aperte o meno. La documentazione ufficiale per l'impostazione sottolinea esplicitamente: aumentarla potrebbe richiedere più memoria condivisa del sistema rispetto a quella consentita dalla configurazione predefinita del sistema operativo (postgresql.org/docs/current/runtime-config-connection.html, accesso il 24 agosto 2026).

Il secondo è variabile e molto più pericoloso su larga scala: work_mem non viene allocato una volta per connessione, ma una volta per operazione di ordinamento o hashing nel piano di query. La documentazione ufficiale è esplicita su questo punto: una query complessa può avviare diverse di queste operazioni in parallelo, e più sessioni possono fare la stessa cosa contemporaneamente, in modo che la memoria effettivamente utilizzata possa valere più volte work_mem (postgresql.org/docs/current/runtime-config-resource.html, ultimo accesso 24 agosto 2026).

Il vero caso peggiore da ricordare

Non è solo max_connections × work_mem a minacciare la memoria di un server. È max_connections × work_mem × numero di operazioni simultanee per query. È questo prodotto che spiega un server che effettua uno scambio o che esaurisce la memoria dopo un aumento di max_connections considerato innocuo.

#
Formula

La formula di dimensionamento del wiki PostgreSQL

Il wiki ufficiale del progetto PostgreSQL documenta una formula di riferimento per calcolare quante connessioni attive il tuo hardware può elaborare in modo efficiente in parallelo, non quante connessioni aperte in totale (wiki.postgresql.org/wiki/Number_Of_Database_Connections, accesso il 24 agosto 2026).

La formula

concorrenza attiva ideale = (core fisici × 2) + dischi efficienti. Il numero di core esclude l'hyperthreading. Il numero di dischi effettivi rimane vicino a 1 sui moderni dispositivi di archiviazione SSD, dove la nozione di disco fisico separato (“mandrino”) perde gran parte del suo significato originale.

Su un server con 8 core fisici e storage SSD, la formula fornisce (8 × 2) + 1 = 17 connessioni attive prima che la velocità effettiva inizi a diminuire. Questo dato spesso sorprende: sembra minuscolo rispetto alle centinaia di connessioni che un'applicazione apre nella pratica. Questo è proprio l'oggetto del paragrafo successivo.

Il numero calcolato dalla formula misura la concorrenza che la CPU e il disco possono assorbire, non il numero di connessioni client che l'applicazione deve aprire. Una flotta di 20 processi applicativi, ciascuno con il proprio pool di 10 connessioni, apre 200 connessioni simultanee a Postgres anche se solo 17 di esse lavorano attivamente in un dato momento. Senza un pooler, max_connections deve coprire 200, non 17. È questo divario che spinge la maggior parte delle architetture ad aggiungere un pooler in modalità transazione, anche se ciò significa scegliere quale (vedi il nostro confronto PgBouncer, Supavisor e PgCat).

#
Procedura

Come modificare max_connections (e perché è necessario un riavvio)

max_connections non viene sostituito a caldo. Questo è un parametro di contesto postmaster: Postgres lo legge una volta, all'avvio, per dimensionare la sua memoria condivisa. Un ricaricamento della configurazione (pg_reload_conf() o SIGHUP) non è sufficiente, è necessario riavviare il server.

Per prima cosa controlla il valore corrente e il suo contesto, per confermare che sarà necessario un riavvio:

psqlsql
SHOW max_connections;

SELECT name, setting, context
FROM pg_settings
WHERE name = 'max_connections';

-- context='postmaster' conferma che è necessario un riavvio

Quindi applica il nuovo valore, quindi riavvia:

psqlsql
ALTER SYSTEM SET max_connections = '300';

-- Scritto in postgresql.auto.conf.
-- Nessun effetto fino al riavvio di Postgres.
terminalbash
# Con systemd
sudo systemctl restart postgresql

# Senza systemd, direttamente con pg_ctl
pg_ctl restart -D $PGDATA -m fast
Un margine che molti dimenticano

max_connections include di default superuser_reserved_connections (3 di default): queste connessioni sono riservate ad un superutente in caso di saturazione, non sono mai disponibili per la tua applicazione, anche se il contatore globale non è ancora stato raggiunto.

#
Codice registrato

Come Aurabase pianifica il budget di max_connections sui suoi cluster Postgres

Il dimensionamento di max_connections non è solo un esercizio teorico. Ecco come Aurabase lo budgeta sui suoi cluster Postgres gestiti:

100
POSTGRES PREDEFINITO
max_connections prima di qualsiasi ottimizzazione
50→400
CUSCINETTI AURABASE DEDICATI
free to enterprise, tramite cluster CNPG
3
SUPERUTENTE RISERVATO
superuser_reserved_connections, impostazione predefinita di Postgres

Cluster dedicati: un cluster Postgres per progetto

A questo livello (vedi il nostro confronto dedicato vs base condivisa), ogni progetto riceve il proprio cluster CloudNativePG e il proprio budget max_connections, dimensionato con la dimensione dell'istanza:

gratuito (dedicato)connessioni_massime 501 istanza · 500 milioni di vCPU · 512 Mi
professionista (predefinito)connessioni_massime 2002 istanze · 1 vCPU · 2Gi
squadraconnessioni_massime 3003 istanze · 2 vCPU · 3Gi
affarimax_connessioni 4003 istanze · 2 vCPU · 4Gi

Cluster condivisi: diversi progetti di un'organizzazione, un budget condiviso

In questo secondo percorso, tutti i progetti della stessa organizzazione si collegano tramite un pooler CNPG (PgBouncer, modalità transaction) davanti ad un primario condiviso:

gratuitoconnessioni_massime 50max_client_conn 100max_user_connections 20
proconnessioni_massime 100max_client_conn 200max_user_connections 60
squadraconnessioni_massime 200max_client_conn 400max_user_connections 150

Tutti i progetti di un'organizzazione si connettono tramite un ruolo applicativo condiviso. max_user_connections quindi da solo limita le connessioni server totali che questo ruolo può aprire nell'intero cluster: questa è la vera salvaguardia globale del cluster, non max_client_conn, che limita solo le connessioni client al pooler stesso.

Tuttavia, questo pooler serve solo il traffico dell'applicazione SDK. PostgREST, dal canto suo, rimane direttamente connesso al primario (servizio-rw): il pooling in modalità transazione interromperebbe il suo meccanismo di ricaricamento dello schema, che ascolta un canale dedicato LISTEN denominato pgrst. Le sue stesse connessioni (2 per replica a livello condiviso, 10 per replica a livello dedicato) contano quindi direttamente nel budget max_connections del primario, al di fuori di qualsiasi pooler, esattamente il tipo di connessione “dimenticata” che il passo 1 della procedura seguente deve includere.

Dati in corso di calibrazione, assunti come tali

Il codice documenta esplicitamente questi budget del pooler condiviso come valori iniziali da calibrare in condizioni reali, misurando pg_stat_activity sotto carico, non come cifre fisse da un benchmark pubblicato. Questa è la stessa disciplina descritta nella nostra metodologia di benchmark : misurare prima di aggiustare, non indovinare e poi sperare. Questi cluster vengono eseguiti su PostgreSQL 16, una scelta documentata nel nostro confronto Postgres 16 vs 17 vs 18.

#
Metodo

La procedura in 5 passaggi per dimensionare max_connections senza pooling

Questa procedura non dipende da nessuno strumento particolare: si applica a qualsiasi server Postgres, gestito o self-hosted.

  1. Conta le tue connessioni client effettive. Numero di processi applicativi moltiplicato per la dimensione del pool interno, più strumenti di amministrazione, replica e monitoraggio. È questo numero, non la formula, che imposta il limite massimo di max_connections.
  2. Calcola la concorrenza ideale del tuo hardware con la formula del wiki PostgreSQL: (core fisici × 2) + dischi efficienti. Questa cifra indica quante di queste connessioni possono effettivamente funzionare in parallelo senza ridurre la velocità effettiva.
  3. Imposta max_connections al di sopra della necessità effettiva per il passaggio 1, con margine per superuser_reserved_connections e per eventuali strumenti di amministrazione che aprono le proprie connessioni all'esterno dell'applicazione.
  4. Applicare la modifica con ALTER SYSTEM SET, quindi riavviare il server. Questo è un parametro postmaster: non è sufficiente un semplice ricaricamento, come spiegato sopra.
  5. Monitora pg_stat_activity nel tempo. Se il numero di connessioni inattive supera di molto il numero di connessioni attive, questo non è un problema di max_connections: è il segnale che è necessario un pooler davanti al server, non un numero più alto.

La richiesta di monitoraggio dal passaggio 5, direttamente utilizzabile:

psqlsql
SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state
ORDER BY count(*) DESC;
#
Segnale di avvertimento

Quando la formula non basta più: i segnali che hai bisogno di un pooler

Tre segnali ritornano sistematicamente quando max_connections da solo non è più sufficiente, qualunque sia il suo valore.

  1. L'errore FATAL: sorry, too many clients already viene visualizzato durante i picchi di carico, mentre la maggior parte delle connessioni visualizzate da pg_stat_activity sono in stato inattivo.
  2. L'applicazione viene eseguita in un ambiente serverless o con lavoratori temporanei (funzioni edge, lavori brevi), che aprono e chiudono connessioni molto più velocemente di quanto il modello processo per connessione di Postgres sia stato progettato per soddisfare.
  3. La formula e la procedura di cui sopra sono già state applicate e l'effettiva necessità di connessioni client continua a superare ciò che la memoria disponibile può allocare senza mettere in pericolo work_mem o shared_buffers.

In these three cases, the correct answer is almost always a pooler positioned between the application and Postgres, not a higher max_connections. Our comparison PgBouncer, Supavisor and PgCat details the three options, and our guide to transaction mode explains the most common compromise once the pooler is in place. For all Postgres tuning beyond connections, see our production Postgres tuning checklist.

#
Domande frequenti

Domande frequenti

Qual è il valore max_connections predefinito di PostgreSQL?+
100, con 3 connessioni riservate al superutente per impostazione predefinita (superuser_reserved_connections). Questa impostazione predefinita è adatta per molte applicazioni che passano attraverso un pooler, ma diventa rapidamente insufficiente senza il pooling non appena un parco di processi applicativi apre ciascuno il proprio batch di connessioni.
Possiamo modificare max_connections senza riavviare PostgreSQL?+
No. max_connections è un parametro di contesto postmaster: Postgres lo legge una volta all'avvio per dimensionare la sua memoria condivisa. ALTER SYSTEM SET scrive il nuovo valore su postgresql.auto.conf, ma lo applica solo un riavvio completo del server; una ricarica o un SIGHUP non bastano.
Quanta memoria consuma una connessione PostgreSQL inattiva?+
Non esiste un unico numero ufficiale: dipende da work_mem, shared_buffers e dalle estensioni caricate per sessione. Ciò che è documentato, tuttavia, è che work_mem viene allocato per operazione di ordinamento o hashing in una query, non per connessione: una singola query complessa può quindi consumare work_mem più volte su una singola connessione attiva.
Dovremmo sempre preferire un pooler come PgBouncer a un max_connections più alto?+
Nella maggior parte dei casi sì, non appena il numero di connessioni client effettive supera notevolmente la concorrenza ideale calcolata dalla formula wiki di PostgreSQL. Un pooler in modalità transazione raggruppa un numero limitato di connessioni fisiche tra un numero molto maggiore di connessioni logiche sul lato applicazione. Guarda il nostro confronto tra PgBouncer, Supavisor e PgCat per scegliere quale.
Cosa misura esattamente la formula (nuclei × 2) + dischi efficienti?+
Stima la concorrenza attiva ideale: il numero di richieste che la CPU e il disco di un determinato server possono elaborare in parallelo senza ridurre il throughput, non il numero totale di connessioni da aprire in max_connections. Questo è un punto di partenza da convalidare mediante misurazione, documentato dal wiki ufficiale del progetto PostgreSQL, non un limite rigido.
Come faccio a sapere se il mio server Postgres è vicino al limite di connessioni?+
Interroga pg_stat_activity e confronta il numero di connessioni in stato attivo con quelle in stato inattivo. Un gran numero di connessioni inattive vicino al limite massimo di max_connections, senza alcuna richiesta attiva dietro di esso, indica quasi sempre la necessità di raggruppare piuttosto che la necessità di aumentare ulteriormente max_connections.

PRONTO PER L'IMPLEMENTAZIONE?

Il tuo backend in cinque minuti.

Nessuna carta di credito richiesta · 500 MB gratuiti · 50.000 MAU