Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Sterownik go-mssqldb korzysta z wbudowanej puli połączeń udostępnianej przez pakiet database/sql języka Go. Każda sql.DB instancja utrzymuje pulę bezczynnych połączeń, które są automatycznie wykorzystywane. Ten artykuł wyjaśnia, jak skonfigurować pulę pod kątem obciążenia.
Jak działa basen
Gdy wywołasz db.QueryContext, db.ExecContext, lub dowolną inną metodę bazy danych:
- Basen próbuje znaleźć bezczynne połączenie.
- Jeśli nie ma dostępnego bezczynnego połączenia, a pula nie osiągnęła jeszcze maksymalnego rozmiaru, tworzone jest nowe połączenie.
- Jeśli pula osiągnęła maksymalną pojemność, wywołanie jest blokowane do momentu, gdy połączenie stanie się dostępne.
- Po zakończeniu operacji połączenie wraca do puli.
Metody konfiguracji puli
Skonfiguruj pulę za pomocą metod dostępnych w elemencie *sql.DB:
| Metoda | Description |
|---|---|
db.SetMaxOpenConns(n) |
Maksymalna liczba otwartych połączeń (w użyciu + bezczynność). Domyślne: 0 (nieograniczone). |
db.SetMaxIdleConns(n) |
Maksymalna liczba bezczynnych połączeń w puli. Wartość domyślna: 2. |
db.SetConnMaxLifetime(d) |
Maksymalny czas ponownego użycia połączenia. Domyślne: 0 (bez limitu). |
db.SetConnMaxIdleTime(d) |
Maksymalny czas, przez jaki połączenie może stać nieczynne, zanim zostanie zamknięte. Domyślne: 0 (bez limitu). |
Example
Skonfiguruj pulę natychmiast po otwarciu bazy danych:
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)
Traktuj te wartości jako punkt wyjścia, a nie uniwersalny domyślny punkt wyjścia. Dla wielu usług pierwszym użytecznym krokiem jest ustawienie limitów dla MaxOpenConns i MaxIdleConns, a następnie dodanie limitów czasu życia i bezczynności tylko wtedy, gdy sposób wdrożenia może prowadzić do nieaktualnych lub nierównomiernie rozłożonych połączeń.
Zalecane ustawienia
| Scenario | MaxOpen | MaxIdle | MaxLifetime | MaxIdleTime |
|---|---|---|---|---|
| Aplikacja webowa na stabilnej ścieżce sieci SQL Server | 25 | 10 | 0 | 0 |
| Aplikacja internetowa za pośrednictwem Azure SQL, bramy lub modułu równoważenia obciążenia | 25 | 10 | 5 minut | 1 minuta |
| Usługi o wysokiej przepustowości | 50-100 | 25 | 5 minut | 30 sekund |
| Zadanie w tle / narzędzie CLI | 5 | 2 | 0 | 0 |
| Azure SQL Database (Basic/Standard) | 10-20 | 5 | 5 minut | 1 minuta |
Wskazówka
Ustaw MaxOpenConns poniżej limitu połączeń dla instancji SQL Server lub warstwy Azure SQL. Przekroczenie maksymalnej liczby jednoczesnych połączeń serwera powoduje awarie logowania dla wszystkich klientów.
Krótkie wartości ConnMaxLifetime i ConnMaxIdleTime zmniejszają ryzyko nieaktualnych połączeń po przełączeniu awaryjnym lub ponownym uruchomieniu bramy, ale jednocześnie zwiększają częstotliwość odnawiania połączeń. Jeśli Twoja aplikacja łączy się bezpośrednio ze stabilną instancją SQL Server i nie występują błędy związane z nieaktualnymi połączeniami, rozsądne jest pozostawienie obu wartości na 0.
Monitorowanie statystyk puli
Użyj db.Stats(), aby odczytać aktualne statystyki puli:
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)
Pola klucza:
| Pole | Description |
|---|---|
OpenConnections |
Całkowite otwarte połączenia (w użyciu + bezczynność). |
InUse |
Połączenia obecnie sprawdzane przez dzwoniących. |
Idle |
Połączenia oczekujące w puli. |
WaitCount |
Łączna liczba razy, gdy dzwoniący musiał czekać na połączenie. |
WaitDuration |
Łączny czas oczekiwania łącznie. |
Jeśli WaitCount stale rośnie, nie zwiększaj automatycznie MaxOpenConns. Najpierw sprawdź, czy wiersze, transakcje i dedykowane połączenia są szybko zamykane oraz potwierdz, że serwer może obsłużyć większą pulę.
SessionInitSQL
Użyj SessionInitSQL, aby uruchomić instrukcję SQL dla każdego nowego połączenia w momencie dodawania go do puli. Ta funkcja jest przydatna do ustawiania opcji na poziomie sesji:
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)
Przypinanie połączenia
Niektóre operacje blokują połączenie, tak aby nie zostało ono zwrócone do puli aż do zakończenia operacji:
-
Transakcje (
db.BeginTx) — Połączenie jest przypisane do czasu wywołaniaCommit()lubRollback(). -
Pojedyncze połączenie (
db.Conn) — Połączenie pozostaje przypisane aż do wywołaniaconn.Close(). -
Otwarte wiersze (
db.QueryContext) — połączenie pozostaje przypięte do momentu wywołaniarows.Close().
Aby nie wyczerpać puli, zawsze niezwłocznie zamykaj te zasoby.
Wykryć i rozwiązać wyczerpanie puli
Wyczerpanie puli połączeń występuje, gdy wszystkie połączenia są używane, a pula osiągnęła MaxOpenConns. Nowi dzwoniący blokują połączenie, dopóki nie zostanie odwrócone. Objawy obejmują wysokie opóźnienia, narastanie gorutyn oraz błędy terminów kontekstowych.
Monitor wyczerpania
Okresowo prowadź ankiety db.Stats() i bądź upozorniony, gdy wykryje się konkurencja:
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)
}
}
}
}
Typowe przyczyny i rozwiązania
| Przyczyna | Objaw | Rozwiązanie |
|---|---|---|
MaxOpenConns zbyt niskie dla obciążenia roboczego |
WaitCount stale rośnie. |
Zwiększ wartość MaxOpenConns. |
| Niezamknięte wiersze w ścieżkach błędów |
InUse rośnie, Idle pozostaje na poziomie 0. |
Użyj defer rows.Close() zaraz po QueryContext. |
| Długotrwałe transakcje |
InUse Utrzymuje się wysoko. |
Utrzymuj transakcje krótkie. Używaj czasowych limitów kontekstowych. |
db.Conn Używana niepotrzebnie |
InUse Wyższy niż się spodziewałem. |
Używaj db.Conn tylko wtedy, gdy potrzebujesz stanu ograniczonego do sesji (tabele tymczasowe). |
MaxOpenConns nie ustawiono (bez ograniczeń) |
Setki otwartych połączeń pod obciążeniem. | Zawsze ustawiaj MaxOpenConns na ograniczoną wartość. |
Kontrole stanu i wykrywanie przestarzałych połączeń
Pula nie weryfikuje aktywnie bezczynnych połączeń. Połączenie, które leżało nieaktywnie podczas recyklingu serwera, zawodzi przy następnym użyciu. Konfiguruj ConnMaxLifetime i ConnMaxIdleTime rotuj połączenia, zanim staną się nieaktualne:
// 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)
Uwaga / Notatka
Gdy ConnMaxIdleTime proaktywnie zamyka bezczynne połączenia, w dziennikach SQL Server może być widoczne ich rozłączanie. To jest oczekiwane zachowanie, a nie wyciek połączeń. Jeśli Twój DBA zgłasza nieoczekiwane zamknięcia połączenia, sprawdź, czy ustawienie ConnMaxIdleTime jest zgodne z oczekiwaniami zespołu dotyczącymi monitoringu.
Jeśli aplikacja łączy się za pośrednictwem modułu równoważenia obciążenia lub usługi Azure SQL z georeplikacją, ustaw wartość ConnMaxLifetime na 5 minut lub mniej. To ustawienie zapewnia redystrybucję połączeń pomiędzy replikami po przełączeniu awaryjnym.
Weryfikacja łączności przy starcie
Zawsze dzwoń db.PingContext po otwarciu bazy danych, aby potwierdzić, czy parametry połączenia jest poprawny i serwer jest dostępny:
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)
}
Metryki puli eksportowej
Udostępnij statystyki puli swojemu systemowi monitorującemu, okresowo odczytując db.Stats():
Przykład Prometeusza
Rejestruj mierniki śledzące statystyki puli i okresowo je aktualizuj:
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 kontrolna konfiguracji puli
| Area | Zalecenie |
|---|---|
MaxOpenConns |
Zawsze ustawiona na wartość ograniczoną. Dopasuj to do współbieżności obciążenia, poniżej limitu połączenia serwera. |
MaxIdleConns |
Ustaw na co najmniej połowę MaxOpenConns. Zbyt mała liczba bezczynnych połączeń powoduje częste obciążenie związane z ponownym nawiązywaniem połączenia. |
ConnMaxLifetime |
Ustaw na 5 minut dla środowisk Azure SQL lub środowisk z równoważeniem obciążenia. Zapobiega powstawaniu przestarzałych połączeń. |
ConnMaxIdleTime |
Ustaw 30-60 sekund na zamknięcie połączeń, które już nie są potrzebne. |
| Nadzorowanie | Monitoruj db.Stats() i otrzymuj alerty o wzroście WaitCount. |
| Oczyszczanie zasobów | Zawsze defer rows.Close(), defer tx.Rollback(), oraz defer conn.Close(). |
| Walidacja startu | Zadzwoń db.PingContext po tym sql.Open , aby potwierdzić łączność. |