Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O controlador go-mssqldb utiliza o pool de ligações integrado fornecido pelo pacote database/sql do Go. Cada sql.DB instância mantém um conjunto de ligações inativas que são reutilizadas automaticamente. Este artigo explica como configurar o pool para a sua carga de trabalho.
Como funciona a piscina
Quando chama db.QueryContext, db.ExecContext ou qualquer outro método de base de dados:
- A piscina tenta encontrar uma ligação ociosa.
- Se não houver ligação ociosa e o pool não tiver atingido o seu tamanho máximo, é criada uma nova ligação.
- Se o conjunto estiver na capacidade máxima, a chamada fica bloqueada até que uma conexão fique disponível.
- Após a conclusão da operação, a ligação é devolvida ao pool.
Métodos de configuração do pool
Configure o pool usando métodos em *sql.DB:
| Método | Description |
|---|---|
db.SetMaxOpenConns(n) |
Número máximo de ligações abertas (em uso + inativa). Predefinição: 0 (ilimitado). |
db.SetMaxIdleConns(n) |
Número máximo de ligações ociosas na piscina. Padrão: 2. |
db.SetConnMaxLifetime(d) |
Tempo total máximo que uma ligação pode ser reutilizada. Padrão: 0 (sem limite). |
db.SetConnMaxIdleTime(d) |
Tempo máximo em que uma ligação pode ficar parada antes de ser fechada. Padrão: 0 (sem limite). |
Exemplo
Configure o pool imediatamente após abrir a base de dados:
db, err := sql.Open("sqlserver", connString)
if err != nil {
log.Fatal(err)
}
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5 * time.Minute)
db.SetConnMaxIdleTime(1 * time.Minute)
Trate estes valores como ponto de partida, não como um padrão universal. Para muitos serviços, o primeiro passo útil é definir limites limitados MaxOpenConns e MaxIdleConns, e depois adicionar limites de vida útil e de inatividade apenas quando o seu caminho de implementação pode deixá-lo com ligações estagnadas ou distribuídas de forma desigual.
Definições recomendadas
| Scenario | MaxOpen | MaxIdle | MaxLifetime | MaxIdleTime |
|---|---|---|---|---|
| Aplicação web num caminho de rede estável do SQL Server | 25 | 10 | 0 | 0 |
| Aplicação web através do SQL do Azure, um gateway ou um balanceador de carga | 25 | 10 | 5 minutos | 1 minuto |
| Serviço de alta capacidade | 50-100 | 25 | 5 minutos | 30 segundos |
| Tarefa em segundo plano / Ferramenta CLI | 5 | 2 | 0 | 0 |
| Base de Dados SQL do Azure (Basic/Standard) | 10-20 | 5 | 5 minutos | 1 minuto |
Tip
Defina MaxOpenConns abaixo do limite de ligação da sua instância do SQL Server ou do nível SQL do Azure. Exceder o máximo de ligações concorrentes do servidor causa falhas de login para todos os clientes.
Valores curtos de ConnMaxLifetime e ConnMaxIdleTime reduzem a probabilidade de ligações inativas após failover ou reciclagem do gateway, mas também aumentam a rotatividade das ligações. Se a sua aplicação estabelecer ligação diretamente a uma instância estável do SQL Server e não estiver a detetar falhas causadas por ligações desatualizadas, é razoável deixar ambos os valores em 0.
Monitorizar estatísticas do pool
Use db.Stats() para ler estatísticas atuais do grupo:
stats := db.Stats()
fmt.Printf("Open: %d, InUse: %d, Idle: %d\n",
stats.OpenConnections, stats.InUse, stats.Idle)
fmt.Printf("WaitCount: %d, WaitDuration: %v\n",
stats.WaitCount, stats.WaitDuration)
Campos-chave:
| Campo | Description |
|---|---|
OpenConnections |
Total de ligações abertas (em uso + inativa). |
InUse |
Ligações atualmente verificadas pelos chamadores. |
Idle |
Conexões em espera no pool. |
WaitCount |
Número total de vezes que um chamador teve de esperar por uma ligação. |
WaitDuration |
Tempo total acumulado de espera. |
Se WaitCount está a crescer de forma constante, não aumente MaxOpenConns automaticamente. Primeiro, verifique se as linhas, transações e ligações dedicadas estão a ser encerradas rapidamente, e confirme que o servidor consegue suportar um pool maior.
SessionInitSQL
Use-se SessionInitSQL para executar uma instrução SQL em cada nova ligação à medida que entra no pool. Esta funcionalidade é útil para definir opções ao nível da sessão:
import (
"database/sql"
"github.com/microsoft/go-mssqldb"
"github.com/microsoft/go-mssqldb/msdsn"
)
config := msdsn.Config{
Host: "<server>",
Port: 1433,
Database: "AdventureWorks2025",
}
connector := mssql.NewConnectorConfig(config)
connector.SessionInitSQL = "SET ANSI_NULLS ON; SET QUOTED_IDENTIFIER ON"
db := sql.OpenDB(connector)
Fixação de ligação
Certas operações fixam uma ligação para que não seja devolvida ao pool até o fim da operação:
-
Transações (
db.BeginTx) - A ligação permanece associada até queCommit()ouRollback()seja chamado. -
Ligações únicas (
db.Conn) - A ligação fica fixa atéconn.Close()ser invocada. -
Linhas abertas (
db.QueryContext) - A ligação é fixada atérows.Close()ser chamada.
Feche sempre estes recursos prontamente para evitar esgotar o pool.
Detetar e resolver o esgotamento da piscina
O esgotamento da piscina ocorre quando todas as ligações estão em uso e a piscina atingiu MaxOpenConns. Os novos chamadores bloqueiam até que a ligação seja retornada. Os sintomas incluem alta latência, acumulação de gorotinas e eventuales erros de prazo de contexto.
Monitorizar sinais de exaustão
Inquérito db.Stats() periodicamente e alerte quando for detetada contenção:
func monitorPool(ctx context.Context, db *sql.DB, interval time.Duration) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
var lastWaitCount int64
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
stats := db.Stats()
newWaits := stats.WaitCount - lastWaitCount
lastWaitCount = stats.WaitCount
if newWaits > 0 {
log.Printf("POOL CONTENTION: %d new waits, avg wait %v, open=%d, inUse=%d, idle=%d",
newWaits, stats.WaitDuration/time.Duration(stats.WaitCount),
stats.OpenConnections, stats.InUse, stats.Idle)
}
}
}
}
Causas comuns e soluções
| Motivo | Symptom | Solução |
|---|---|---|
MaxOpenConns demasiado baixo para a carga de trabalho |
WaitCount cresce de forma constante. |
Aumente MaxOpenConns. |
| Linhas não fechadas em caminhos de erro |
InUse cresce, Idle mantém-se em 0. |
Use defer rows.Close() imediatamente após QueryContext. |
| Transações de longa duração |
InUse mantém-se alto. |
Mantenha as transações curtas. Utilize tempos limite de contexto. |
db.Conn usado desnecessariamente |
InUse mais alto do que o esperado. |
Só usa db.Conn quando precisares de estado com âmbito de sessão (tabelas temporárias). |
MaxOpenConns não definido (ilimitado) |
Centenas de ligações abertas sob carga. | Defina sempre MaxOpenConns para um valor limitado. |
Verificações de saúde e deteção de ligações obsoletas
O pool não valida ativamente as ligações inativas. Uma ligação que ficou inativa enquanto o servidor a reciclava falha na próxima utilização. Configure ConnMaxLifetime e ConnMaxIdleTime para alternar as ligações antes de ficarem inativas:
// Rotate connections every 5 minutes to stay compatible
// with load balancers and Azure SQL failover.
db.SetConnMaxLifetime(5 * time.Minute)
// Close connections that have been idle for over 1 minute
// to reduce the number of stale connections.
db.SetConnMaxIdleTime(1 * time.Minute)
Note
Quando ConnMaxIdleTime fecha ligações ociosas de forma proativa, os registos do SQL Server podem mostrar ligações a serem cortadas. Este comportamento é esperado, não uma fuga de conexão. Se o seu DBA indicar encerramentos inesperados de ligações, verifique se a definição ConnMaxIdleTime está alinhada com as expectativas de monitorização da equipa.
Se a sua aplicação se ligar através de um balanceador de carga ou do SQL do Azure com geo-replicação, defina ConnMaxLifetime para 5 minutos, ou menos. Esta configuração garante que as ligações são redistribuídas entre réplicas após um failover.
Validar a conectividade no arranque
Ligue db.PingContext sempre após abrir a base de dados para confirmar que a cadeia de ligação está correta e que o servidor está acessível:
db, err := sql.Open("sqlserver", connString)
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
log.Fatalf("Cannot connect to database: %v", err)
}
Exportar métricas do conjunto
Exponha as estatísticas do pool ao seu sistema de monitorização, lendo periodicamente db.Stats():
Exemplo de Prometheus
Registe medidores que acompanham as estatísticas do pool e atualize-os periodicamente:
import "github.com/prometheus/client_golang/prometheus"
var (
dbOpenConns = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "db_open_connections",
Help: "Number of open database connections.",
})
dbInUseConns = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "db_in_use_connections",
Help: "Number of connections currently in use.",
})
dbWaitCount = prometheus.NewCounter(prometheus.CounterOpts{
Name: "db_wait_count_total",
Help: "Total number of times a caller waited for a connection.",
})
dbWaitDuration = prometheus.NewCounter(prometheus.CounterOpts{
Name: "db_wait_duration_seconds_total",
Help: "Total wait time for a connection.",
})
)
func init() {
prometheus.MustRegister(dbOpenConns, dbInUseConns, dbWaitCount, dbWaitDuration)
}
func recordPoolMetrics(ctx context.Context, db *sql.DB) {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
var lastWaitCount int64
var lastWaitDuration time.Duration
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
stats := db.Stats()
dbOpenConns.Set(float64(stats.OpenConnections))
dbInUseConns.Set(float64(stats.InUse))
dbWaitCount.Add(float64(stats.WaitCount - lastWaitCount))
dbWaitDuration.Add((stats.WaitDuration - lastWaitDuration).Seconds())
lastWaitCount = stats.WaitCount
lastWaitDuration = stats.WaitDuration
}
}
}
Lista de verificação da configuração do pool
| Area | Recommendation |
|---|---|
MaxOpenConns |
Sempre definido para um valor limitado. Ajuste-a ao nível de concorrência da sua carga de trabalho, mantendo-se abaixo do limite de ligações do servidor. |
MaxIdleConns |
Defina como pelo menos metade de MaxOpenConns. Poucas ligações em repouso causam sobrecarga frequente de religação. |
ConnMaxLifetime |
Defina para 5 minutos para SQL do Azure ou ambientes com balanceamento de carga. Evita a acumulação de ligações obsoletas. |
ConnMaxIdleTime |
Defina para 30-60 segundos para fechar ligações que já não são necessárias. |
| Monitoring | Monitorize db.Stats() e configure alertas para o crescimento de WaitCount. |
| Limpeza de recursos | Sempre defer rows.Close(), defer tx.Rollback(), e defer conn.Close(). |
| Validação de arranque | Ligue db.PingContext depois sql.Open para verificar a conectividade. |