go-mssqldb ile bağlantı havuzlama

Sürücü, go-mssqldb Go'nun database/sql paketi tarafından sağlanan yerleşik bağlantı havuzunu kullanır. sql.DB örneğinin her biri, otomatik olarak yeniden kullanılan boştaki bağlantılardan oluşan bir havuzu korur. Bu makale, iş yükünüz için havuzu nasıl yapılandıracağınızı açıklar.

Havuz nasıl çalışır

db.QueryContext, db.ExecContext ya da başka herhangi bir veritabanı yöntemini çağırdığınızda:

  1. Havuz boş bir bağlantı bulmaya çalışıyor.
  2. Boşta bağlantı yoksa ve havuz maksimum boyutuna ulaşmadıysa yeni bir bağlantı oluşturulur.
  3. Havuz maksimum kapasitedeyse, çağrı bağlantı sağlanana kadar bloklanır.
  4. İşlem tamamlandıktan sonra bağlantı havuza geri döner.

Havuz yapılandırma yöntemleri

Havuzu aşağıdaki *sql.DByöntemlerle yapılandırmak için :

Method Description
db.SetMaxOpenConns(n) Açık bağlantıların maksimum sayısı (kullanılıyor + boşta). Varsayılan: 0 (sınırsız).
db.SetMaxIdleConns(n) Havuzdaki maksimum boşta bağlantı sayısı. Varsayılan: 2.
db.SetConnMaxLifetime(d) Bir bağlantının yeniden kullanılabileceği maksimum toplam süre. Varsayılan: 0 (sınır yok).
db.SetConnMaxIdleTime(d) Bir bağlantının kapanmadan önce boşta kalabileceği maksimum süre. Varsayılan: 0 (sınır yok).

Example

Veritabanını açtıktan hemen sonra havuzu yapılandırın:

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)

Bu değerleri evrensel bir varsayılan değil, bir başlangıç noktası olarak ele alın. Birçok hizmet için ilk yararlı adım, sınırlı MaxOpenConns ve MaxIdleConns değerlerini ayarlamaktır; bağlantı ömrü ve boşta kalma sınırlarını ise yalnızca dağıtım sürecinizin eskimiş veya dengesiz dağılmış bağlantılara yol açabildiği durumlarda ekleyin.

Scenario MaxOpen MaxIdle MaxLifetime MaxIdleTime
Kararlı bir SQL Server ağ yolu üzerindeki web uygulaması 25 10 0 0
Azure SQL, bir ağ geçidi veya yük dengeleyici aracılığıyla web uygulaması 25 10 5 dakika 1 dakika
Yüksek verimli hizmet 50-100 25 5 dakika 30 saniye
Arka plan işi / CLI aracı 5 2 0 0
Azure SQL Veritabanı (Basic/Standard) 10-20 5 5 dakika 1 dakika

Tip

SQL Server örneğinizin veya Azure SQL katmanınızın bağlantı sınırının altına ayarlayınMaxOpenConns. Sunucunun maksimum eşzamanlı bağlantılarını aşmak, tüm istemcilerde giriş hatalarına yol açar.

ConnMaxLifetime ve ConnMaxIdleTime değerlerinin kısa tutulması, yük devretme veya ağ geçidi geri dönüştürme sonrasında eskimiş bağlantıların oluşma olasılığını azaltır, ancak aynı zamanda bağlantı değişim sıklığını da artırır. Uygulamanız doğrudan kararlı bir SQL Server örneğine bağlanıyorsa ve bağlantı arızaları görmüyorsanız, her iki değeri de aynı 0 şekilde bırakmak makul.

Havuz istatistiklerini izleme

Mevcut havuz istatistiklerini okumak için kullanın db.Stats() :

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)

Anahtar alanları:

Alan Description
OpenConnections Toplam açık bağlantılar (kullanımda + boşta).
InUse Şu anda arayanlar tarafından bağlantılar kontrol ediliyor.
Idle Havuzda bağlantılar bekliyor.
WaitCount Bir arayanın bağlantı için beklemek zorunda kaldığı toplam sayı.
WaitDuration Toplam birikimli bekleme süresi.

Eğer WaitCount istikrarlı bir şekilde artıyorsa, MaxOpenConns değerini otomatik olarak artırmayın. Öncelikle, satırların, işlemlerin ve özel bağlantıların hızlıca kapatıldığını ve sunucunun daha büyük bir havuzu destekleyebileceğini doğrulayın.

SessionInitSQL

Her yeni bağlantı havuzuna girdiğinde bir SQL deyimi çalıştırmak için SessionInitSQL kullanın. Bu özellik, oturum seviyesi seçenekleri ayarlamak için faydalıdır:

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)

Bağlantı sabitleme

Bazı işlemler, bir bağlantıyı işlem sona erene kadar havuza geri döndürülmeyecek şekilde sabitler:

  • İşlemler (db.BeginTx) - Bağlantı, Commit() veya Rollback() çağrılana kadar sabitlenmiş kalır.
  • Tek bağlantılar (db.Conn) - conn.Close() çağrılana kadar bağlantı sabit tutulur.
  • Açık satırlar (db.QueryContext) - rows.Close() çağrılana kadar bağlantı sabitlenir.

Havuzu aç bırakmamak için bu kaynakları her zaman hızlıca kapatın.

Havuz tükenmesini tespit ve çözümle

Bağlantı havuzunun tükenmesi, tüm bağlantılar kullanımdayken ve havuz MaxOpenConns noktasına ulaştığında gerçekleşir. Yeni arayanlar, bağlantı geri dönene kadar blok yapar. Belirtiler arasında yüksek gecikme, rutin birikimi ve nihayetinde ortaya çıkan bağlam teslim tarihi hataları bulunur.

Yorgunluğu izleme

Aralıklı anket db.Stats() yapın ve çatışma tespit edildiğinde uyarın:

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)
            }
        }
    }
}

Yaygın nedenler ve çözümler

Cause Belirti Çözüm
MaxOpenConns İş yükü için çok düşük WaitCount istikrarlı bir şekilde büyür. Artırın MaxOpenConns.
Hata yollarında kapalı olmayan satırlar InUse büyür, Idle sıfırda kalır. QueryContext öğesinden hemen sonra defer rows.Close() kullanın.
Uzun süreli işlemler InUse yüksek kalmaya devam eder. İşlemleri kısa tutun. Bağlam zaman aşımı kullanın.
db.Conn gereksiz yere kullanılmıştır InUse beklenenden daha yüksek. db.Conn yalnızca oturum kapsamındaki duruma (geçici tablolar) ihtiyaç duyduğunuzda kullanın.
MaxOpenConns Set Değil (Sınırsız) Yük altında yüzlerce açık bağlantı. Her zaman MaxOpenConns değerini sınırlı bir değere ayarlayın.

Sağlık kontrolleri ve bayat bağlantı tespiti

Havuz, boşta bağlantıları aktif olarak doğrulamaz. Sunucu yeniden başlatırken boşta kalan bir bağlantı, bir sonraki kullanımda başarısız olur. ConnMaxLifetime ve ConnMaxIdleTime yapılandırarak bağlantıları eskimeden önce döndürün:

// 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)

Uyarı

ConnMaxIdleTime Boşta bağlantıları proaktif olarak kapattığında, SQL Server logları bağlantıların kesildiğini gösterebilir. Bu beklenen davranıştır, bağlantı sızıntısı değil. DBA'nız beklenmedik bağlantı kapanmalarını bildiriyorsa, ayarın ConnMaxIdleTime ekibin izleme beklentileriyle uyumlu olduğundan emin olun.

Uygulamanız bir yük dengeleyici veya Azure SQL ile jeo-replikasyon ile bağlanıyorsa, 5 dakika veya daha kısa süreye ayarlayınConnMaxLifetime. Bu ayar, bir failover sonrası bağlantıların replikalar arasında yeniden dağıtılmasını sağlar.

Bağlantıyı başlatmada doğrulama

Veritabanını açtıktan sonra her zaman bağlantı dizesi'in doğru olup olmadığını ve sunucunun ulaşılabilir olduğunu doğrulamak için arayındb.PingContext:

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)
}

İhracat havuzu metrikleri

db.Stats() öğesini düzenli aralıklarla okuyarak havuz istatistiklerini izleme sisteminize aktarın:

Prometheus örneği

Havuz istatistiklerini takip eden gauge'leri kaydedin ve periyodik olarak güncelleyin:

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
        }
    }
}

Havuz yapılandırma kontrol listesi

Area Recommendation
MaxOpenConns Her zaman sınırlı bir değere ayarlanır. Bunu iş yükünüzün eşzamanlılığına, sunucunun bağlantı sınırının altına eşleştirin.
MaxIdleConns En az MaxOpenConns öğesinin yarısına ayarlayın. Çok az sayıda atıl bağlantı, çok sık yeniden bağlanma ek yüküne neden olur.
ConnMaxLifetime Azure SQL veya yük dengeli ortamlar için 5 dakikaya ayarlanabilir. Eski bağlantıların birikmesini önler.
ConnMaxIdleTime Artık gerekmeyen bağlantıları kapatmak için 30-60 saniyeye ayarlayın.
İzleme db.Stats() yoklayın ve WaitCount büyümesi için uyarı oluşturun.
Kaynak temizleme Her zaman defer rows.Close(), defer tx.Rollback(), ve defer conn.Close().
Başlangıç doğrulaması Bağlantıyı doğrulamak için sql.Open işleminden sonra db.PingContext öğesini çağırın.