database/sql Connection Pooling & Timeouts
*sql.DB é um pool de conexões, não um único socket de longa duração.
Busque em todas as páginas da documentação
*sql.DB é um pool de conexões, não um único socket de longa duração.
Ajustar SetMaxOpenConns, configurações de idle e tempos de vida impede que seu serviço cause inanição no banco de dados ou trave goroutines esperando por uma conexão livre.
Combine limites de pool com deadlines de context.Context para que requisições HTTP abandonadas liberem slots do pool prontamente.
SetMaxOpenConns limita conexões simultâneas por processo.
SetMaxIdleConns controla quantas conexões permanecem aquecidas entre picos.
SetConnMaxLifetime e SetConnMaxIdleTime rotacionam conexões para higiene de balanceador de carga e credenciais.
QueryContext e funções similares honram o cancelamento enquanto esperam por uma conexão ou executam uma consulta.
Cartão de receita de referência rápida - pronto para copiar e colar.
func ConfigurePool(db *sql.DB) {
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(30 * time.Minute)
db.SetConnMaxIdleTime(5 * time.Minute)
}
func GetOrder(ctx context.Context, db *sql.DB, id string) (string, error) {
ctx, cancel := context.WithTimeout(ctx, 300*time.Millisecond)
defer cancel()
var status string
err := db.QueryRowContext(ctx,
`SELECT status FROM orders WHERE id = $1`, id,
).Scan(&status)
return status, err
}Quando usar isso:
database/sql por trás de HTTP ou gRPC.WaitCount aumentando ou consultas excedendo o tempo limite do desconectado do cliente.max_connections do banco de dados pelo número de pods.package main
import (
"context"
"database/sql"
"fmt"
"time"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
panic(err)
}
defer db.Close()
db.SetMaxOpenConns(5)
db.SetMaxIdleConns(2)
db.SetConnMaxLifetime(time.Hour)
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
var one int
err = db.QueryRowContext(ctx, `SELECT 1`).Scan(&one)
fmt.Println(one, err)
s := db.Stats()
fmt.Printf("waitCount=%d waitDuration=%s\n", s.WaitCount, s.WaitDuration)
}O que isso demonstra:
QueryRowContext respeita o deadline pai.db.Stats() expõe WaitCount e WaitDuration para sinais de saturação.sql.Open cria um gerenciador de pool; conexões são criadas preguiçosamente até MaxOpenConns.QueryContext bloqueiam até que uma conexão seja liberada ou ctx termine.MaxIdleConns; caso contrário, elas são fechadas.ConnMaxLifetime força a substituição mesmo que a conexão esteja saudável, o que ajuda por trás de credenciais rotativas ou PgBouncer.| Configuração | Efeito | Ponto de partida |
|---|---|---|
SetMaxOpenConns | Limite rígido por processo | floor(db_max / replicas) |
SetMaxIdleConns | Conexões aquecidas mantidas | Frequentemente MaxOpenConns / 2 |
SetConnMaxLifetime | Idade máxima antes da reciclagem | 15-60 minutos atrás do LB |
SetConnMaxIdleTime | Fecha ocioso após a duração | 2-10 minutos |
PingContext | Verificação de integridade na inicialização | Necessário em main |
ctx, cancel := context.WithTimeout(parent, 200*time.Millisecond)
defer cancel()
_, err := db.ExecContext(ctx, `SET statement_timeout = 150`)statement_timeout do Postgres protege quando os drivers terminam o pacote atual lentamente.*sql.DB por DSN por processo é o típico; não abra um novo pool por requisição.*sql.DB de main para repositórios; evite pools em init() sem testes.db.Stats() (InUse, Idle, WaitCount) em um timer em produção.MaxOpenConns padrão ilimitado - Um pico de tráfego pode abrir milhares de conexões e atingir too many connections do Postgres. Correção: defina um limite explícito com base no planejamento de capacidade.MaxIdleConns enorme igual a MaxOpenConns em DBs minúsculos - Sockets ociosos desperdiçam slots do DB. Correção: pool ocioso menor que o limite aberto, a menos que os dados de latência digam o contrário.Ping na inicialização - Credenciais incorretas falham na primeira requisição do usuário. Correção: PingContext com timeout de boot em main.r.Context().WaitCount - A saturação parece consultas lentas. Correção: alerte sobre o crescimento da duração de espera antes que o tempo de consulta p99 dispare.Close - CI esgota portas efêmeras. Correção: um pool por pacote de teste ou contêiner de teste compartilhado com teardown.| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| PgBouncer / RDS Proxy | Muitos serviços pequenos compartilham um DB | Você precisa de persistência de prepared stmt em nível de sessão sem cuidado com o modo de transação |
Apenas statement_timeout | O cancelamento do driver é fraco | Substituir completamente o contexto com escopo de requisição |
Conexão única (MaxOpenConns=1) | Bloqueios de arquivo SQLite, ferramentas minúsculas | Handlers HTTP concorrentes |
| Pool gerenciado por ORM | Equipe já padronizada em GORM | Você precisa de db.Stats() explícito em runbooks de operações |
sql.DB por esquema | Isolamento forte multi-tenant | Um serviço com um usuário de role é suficiente |
Divida max_connections do banco de dados pelo número de réplicas, subtraia espaço para administração e migração, e então limite por processo.
Meça WaitCount e ajuste.
Ele impede novos usos após o deadline; o trabalho em andamento termina, a menos que o contexto o cancele.
Combine com tempos de vida abaixo dos timeouts de idle do balanceador de carga.
Nem sempre - conexões ociosas consomem slots do DB.
Comece com metade do máximo aberto e ajuste com métricas.
Se nenhuma conexão estiver livre, QueryContext espera até que ctx termine e retorna context.DeadlineExceeded.
É por isso que o ctx do handler é importante para a saúde do pool.
Não - pools têm escopo de processo.
Requisições emprestam e retornam conexões rapidamente.
Defina MaxOpenConns(1), execute duas consultas concorrentes, afirme que a segunda respeita um timeout curto de ctx.
Use httptest mais errgroup em testes de integração.
Bancos de dados de arquivo serializam escritas; ainda assim, defina limites razoáveis para paralelismo de testes em memória.
Bancos de dados SQL em rede se beneficiam mais do ajuste.
OpenConnections, InUse, Idle, WaitCount, WaitDuration e histogramas de latência de consulta marcados pelo nome da instrução.
Sim - injete o mesmo *sql.DB em ambos os servidores em main.
Pools separados apenas para DSNs diferentes ou requisitos de isolamento.
O r.Context() do handler deve ser o pai de cada QueryContext.
Versões da Stack: Esta página foi escrita para Go 1.26.x (padrão GC Green Tea, go fix modernizers - verifique o patch na compilação), chi (última versão - verifique na compilação), gin (última versão - verifique na compilação), echo (última versão - verifique na compilação), google.golang.org/grpc (última versão - verifique na compilação), sigs.k8s.io/controller-runtime (última versão - verifique na compilação), kubebuilder (última versão - verifique na compilação), tinygo (última versão - verifique os alvos de placa na compilação), wazero (última versão - verifique na compilação) e golangci-lint (última versão - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 19 de jul. de 2026