Objetivos de aprendizado
Depois de ler este artigo, você vai conseguir:
- Ler as mensagens de erro do MySQL e do PostgreSQL e entender a qual limite elas apontam
- Descobrir quem está segurando as suas conexões
- Configurar um pool de conexões com um tamanho seguro para a sua aplicação
Sua aplicação funcionava ontem. Hoje ela quebra ao iniciar, ou falha de forma aleatória sob carga, com uma mensagem dizendo que o banco não tem lugar para você. O banco não caiu. Ele só tem um número fixo de vagas de conexão, e alguma coisa ocupou todas.
Esse é um dos erros de produção mais comuns em bots, APIs e sites, e quase sempre é um problema do lado do cliente: processos demais, nenhum pool ou conexões que nunca são fechadas.
Como o erro aparece?#
O MySQL (e o MariaDB) reportam o erro 1040:
ERROR 1040 (HY000): Too many connectionsNo Node.js com mysql2, aparece o mesmo texto com o código ER_CON_COUNT_ERROR.
O PostgreSQL tem duas variantes, ambas com SQLSTATE 53300:
FATAL: sorry, too many clients already
FATAL: remaining connection slots are reserved for roles with the SUPERUSER attributeA primeira indica que o servidor chegou ao max_connections. A segunda indica que só restam as vagas reservadas para superusuários: papéis comuns ficam de fora, mas um administrador ainda consegue se conectar. Antes do PostgreSQL 16, a segunda mensagem dizia "remaining connection slots are reserved for non-replication superuser connections".
O que é uma conexão com o banco e por que ela é limitada?#
Uma conexão é uma sessão ativa entre um cliente e o servidor. No PostgreSQL cada uma é um processo separado no servidor; no MySQL cada uma ganha uma thread. Nos dois casos ela consome memória e estruturas internas do servidor, mesmo ociosa.
Por isso o servidor limita a quantidade com max_connections:
- PostgreSQL: o padrão costuma ser 100, e o
superuser_reserved_connectionsguarda 3 delas. Mudar omax_connectionsexige reiniciar o servidor. - MySQL: o padrão é 151. O servidor aceita
max_connections+ 1 clientes, e a vaga extra é para contas com o privilégioCONNECTION_ADMIN, para que um administrador ainda consiga se conectar e investigar o problema.
Aumentar esse número não sai de graça, porque cada vaga a mais consome recursos do servidor.
Por que as conexões acabam?#
Da causa mais comum para a menos comum:
- Sem pool, uma conexão por requisição. Código que abre um cliente novo a cada requisição ou evento multiplica as conexões pelo tráfego.
- Conexões vazadas. O código pega um cliente do pool e nunca o devolve, muitas vezes porque um caminho de erro pula o
release()ou oclose(). - Processos demais. Cada processo tem o seu próprio pool. Dez instâncias com pool de 10 dão 100 conexões, o padrão inteiro do PostgreSQL.
- Aplicações serverless ou que reiniciam sozinhas. Cada cold start ou loop de crash abre conexões novas antes que as antigas expirem no servidor.
- Transações longas e consultas lentas. As conexões ficam ocupadas, então o pool precisa de mais delas.
- Outros clientes. Ferramentas de administração, jobs de migration, scripts de cron e dashboards também contam para o mesmo limite.
Como ver quem está usando as conexões?#
No PostgreSQL, com um papel que tenha permissão para ver as outras sessões:
SHOW max_connections;
SELECT count(*) AS total FROM pg_stat_activity;
SELECT usename, application_name, state, count(*)
FROM pg_stat_activity
GROUP BY usename, application_name, state
ORDER BY count(*) DESC;Muitas linhas no estado idle com o mesmo application_name são o sinal clássico de vazamento ou de pool grande demais.
No MySQL:
SHOW VARIABLES LIKE 'max_connections';
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';
SHOW FULL PROCESSLIST;O Max_used_connections é o pico desde o último reinício. Se ele for igual ao max_connections, você bateu no teto pelo menos uma vez. As entradas Sleep na lista de processos são conexões ociosas.
Como resolver com pool de conexões?#
Um pool abre um número pequeno de conexões uma vez e as empresta para o seu código. As consultas esperam um instante por uma conexão livre em vez de abrir outra. Crie o pool uma vez na inicialização, compartilhe com o processo inteiro e nunca o crie dentro de um handler de requisição.
Com o pg (node-postgres), cujo pool tem max padrão de 10:
// db.js
import pg from "pg";
export const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
max: 5, // limite máximo para ESTE processo
idleTimeoutMillis: 10_000, // fecha clientes ociosos depois de 10 s
connectionTimeoutMillis: 5_000, // falha rápido em vez de esperar para sempre
});
// Para consultas avulsas, deixe o pool cuidar de pegar e devolver:
const { rows } = await pool.query("SELECT now()");
console.log(rows[0]);
// Para transações, segure um cliente e SEMPRE devolva:
const client = await pool.connect();
try {
await client.query("BEGIN");
await client.query("UPDATE accounts SET balance = balance - 10 WHERE id = $1", [1]);
await client.query("COMMIT");
} catch (err) {
await client.query("ROLLBACK");
throw err;
} finally {
client.release();
}Não rode uma transação pelo pool.query, porque cada chamada pode cair num cliente diferente.
Com o mysql2:
import mysql from "mysql2/promise";
export const pool = mysql.createPool({
uri: process.env.DATABASE_URL,
connectionLimit: 5,
waitForConnections: true,
});
const [rows] = await pool.query("SELECT NOW() AS now");
console.log(rows[0]);Em Python funciona do mesmo jeito: crie um engine (SQLAlchemy) ou pool na importação do módulo e reaproveite.
Qual deve ser o tamanho do pool?#
Faça a multiplicação antes de escolher um número:
total de conexões = processos x tamanho do poolSe você roda 4 processos com pool de 5, usa 20 conexões. Some uma folga para migrations, um cliente SQL que você abre na mão e jobs de cron, e mantenha o total bem abaixo do limite. Um pool de 5 a 10 por processo costuma bastar, porque cada conexão só fica ocupada enquanto a consulta roda e, por isso, atende muitas requisições por segundo.
Se um único processo realmente precisa de centenas de consultas simultâneas, o gargalo provavelmente é o banco, e mais conexões não vão deixá-lo mais rápido.
Quando aumentar o limite ou adicionar o PgBouncer?#
Aumente o max_connections quando os seus pools já estão bem dimensionados e você simplesmente tem mais processos do que vagas. No MySQL dá para mudar com o servidor rodando:
SET GLOBAL max_connections = 300; -- perdido no reinício
SET PERSIST max_connections = 300; -- sobrevive ao reinícioNo PostgreSQL, edite o postgresql.conf e reinicie. Lembre que mais conexões usam mais memória.
Quando você tem muitos processos ou clientes de vida curta, coloque um pooler como o PgBouncer na frente do PostgreSQL. No modo de transaction pooling, uma conexão do servidor é atribuída só durante uma transação, então um conjunto pequeno de conexões do servidor atende muito mais clientes. A contrapartida: recursos que dependem de estado de sessão, como SET, LISTEN, advisory locks em nível de sessão e o PREPARE em SQL, não funcionam de forma confiável nesse modo.
Como evitar que volte?#
- Crie um pool por processo, na inicialização.
- Devolva as conexões em blocos
finallye feche o pool no desligamento compool.end(). - Configure timeouts de ociosidade e de aquisição para que os problemas falhem de forma visível.
- Dê a cada aplicação o seu próprio usuário do banco ou
application_name, para saber quem está conectado. - Crie um alerta quando o
Threads_connectedou a contagem de linhas dopg_stat_activityse aproximar de 80% do limite.
Em dúvida sobre qual motor serve para o seu projeto? Como escolher um banco de dados compara as opções, o que é o PostgreSQL explica o motor em si e como hospedar um banco de dados mostra o passo a passo para colocar um no ar.
Como fazer isso na Square Cloud#
Na Square Cloud você cria o banco no painel e faz o deploy das suas aplicações, então as configurações do pool ficam no seu código e nas variáveis de ambiente. A hospedagem de MongoDB, PostgreSQL, MySQL e Redis começa no plano Standard. Os bancos rodam no mesmo datacenter das aplicações, com TLS obrigatório.
Cada banco aceita até 512 conexões e não há pooler na frente, então quem controla o número de conexões é o pool da sua aplicação. Guarde a URL de conexão numa variável de ambiente de cada aplicação e dimensione o pool de cada processo como mostrado acima. Some os pools de todas as aplicações que apontam para o mesmo banco e mantenha o total com folga abaixo desse teto.
Cada reinício abre o pool de novo. O reinício automático é opcional (vem ligado no deploy pelo painel) e só age quando o processo encerra com erro. Se a aplicação entrar em loop de crash, cada volta abre um pool novo, então procure nos logs a causa do erro e corrija.
Para conhecer os motores e os planos, veja a página de hospedagem de bancos de dados.
Hospedagem de bancos
MongoDB, PostgreSQL, MySQL e Redis no mesmo datacenter do app
Perguntas frequentes
Qual é o max_connections padrão no PostgreSQL e no MySQL?
O PostgreSQL usa 100 por padrão (pode ser menos se o kernel não suportar) e reserva 3 vagas para superusuários. O MySQL usa 151 por padrão, mais uma vaga extra para contas de administrador.
Devo simplesmente aumentar o max_connections?
Só depois de procurar vazamentos e adicionar um pool. Cada conexão custa memória no servidor, e o PostgreSQL precisa de reinício para mudar o valor. Um limite maior muitas vezes só adia o mesmo erro.
Quantas conexões um pool deve abrir?
Comece pequeno, algo entre 5 e 10 por processo da aplicação, e multiplique pelo número de processos e instâncias. O total precisa ficar abaixo do limite do banco, com folga para ferramentas de administração e migrations.
Qual a diferença entre um pool na minha aplicação e o PgBouncer?
Um pool dentro da aplicação compartilha conexões dentro de um processo. O PgBouncer fica na frente do PostgreSQL e compartilha um conjunto menor de conexões do servidor entre muitos processos ou servidores.
Mais em Bancos de dados
Ver todos os 24 artigos- Quanto custa um banco de dados na nuvem?Um banco de dados na nuvem custa de zero a centenas de dólares por mês. Veja o que define o preço: instância, disco, tráfego, snapshots e como estimar o seu.
- Como hospedar um banco de dados (e conectar sua aplicação)Para hospedar um banco de dados, escolha o motor, crie a instância, guarde a string de conexão em variável de ambiente e teste a conexão. Veja o passo a passo.
- O que é Redis e para que serveRedis é um banco de dados em memória que guarda pares chave-valor e estruturas de dados. Veja como funciona, para que serve e quando não usar.
- Como fazer backup do banco de dados (MySQL, PostgreSQL, MongoDB)Faça backup de MySQL, PostgreSQL e MongoDB com mysqldump, pg_dump e mongodump, e restaure o dump. Veja os comandos, como verificar e os erros comuns.