Pengumpulan koneksi dengan go-mssqldb

Driver go-mssqldb menggunakan kumpulan koneksi bawaan yang disediakan oleh paket Go database/sql . Setiap sql.DB instans mempertahankan kumpulan koneksi menganggur yang digunakan kembali secara otomatis. Artikel ini menjelaskan cara mengonfigurasi kumpulan untuk beban kerja Anda.

Cara kerja kolam renang

Saat Anda memanggil db.QueryContext, db.ExecContext, atau metode database lainnya:

  1. Kumpulan mencoba menemukan koneksi yang tidak aktif.
  2. Jika tidak ada koneksi menganggur yang tersedia dan kumpulan belum mencapai ukuran maksimumnya, koneksi baru akan dibuat.
  3. Jika kumpulan berada pada kapasitas maksimum, panggilan diblokir hingga koneksi tersedia.
  4. Setelah operasi selesai, koneksi dikembalikan ke pool.

Metode konfigurasi kumpulan

Konfigurasikan kumpulan menggunakan metode pada *sql.DB:

Metode Description
db.SetMaxOpenConns(n) Jumlah maksimum koneksi terbuka (digunakan + tidak aktif). Default: 0 (tidak terbatas).
db.SetMaxIdleConns(n) Jumlah maksimum koneksi yang tidak aktif dalam pool. Standar: 2.
db.SetConnMaxLifetime(d) Total waktu maksimum koneksi dapat digunakan kembali. Default: 0 (tanpa batas).
db.SetConnMaxIdleTime(d) Waktu maksimum koneksi dapat tetap tidak aktif sebelum ditutup. Default: 0 (tanpa batas).

Example

Konfigurasikan kumpulan segera setelah membuka database:

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)

Perlakukan nilai-nilai ini sebagai titik awal, bukan default universal. Untuk banyak layanan, langkah pertama yang berguna adalah menetapkan batas terbatas MaxOpenConns dan MaxIdleConns, lalu menambahkan batas masa pakai dan idle hanya ketika jalur penyebaran Anda dapat membuat Anda memiliki koneksi kedaluwarsa atau tidak merata.

Skenario MaxOpen MaxIdle Masa Pakai Maks MaxIdleTime
Aplikasi web pada jalur jaringan SQL Server yang stabil 25 10 0 0
Aplikasi web melalui Azure SQL, gateway, atau penyeimbang beban 25 10 5 menit 1 menit
Layanan dengan throughput tinggi 50-100 25 5 menit 30 detik
Pekerjaan latar belakang / alat CLI 5 2 0 0
Azure SQL Database (Dasar/Standar) 10-20 5 5 menit 1 menit

Tip

Tetapkan MaxOpenConns di bawah batas koneksi instans SQL Server atau tingkat Azure SQL Anda. Melebihi koneksi bersamaan maksimum server menyebabkan kegagalan login untuk semua klien.

Nilai ConnMaxLifetime dan ConnMaxIdleTime yang pendek mengurangi kemungkinan koneksi usang setelah failover atau gateway dimulai ulang, tetapi juga meningkatkan frekuensi putus-sambung koneksi. Jika aplikasi Anda terhubung langsung ke instans SQL Server yang stabil dan Anda tidak mengalami kegagalan akibat koneksi usang, membiarkan kedua nilai tetap di 0 adalah hal yang wajar.

Memantau statistik kumpulan

Gunakan db.Stats() untuk membaca statistik kumpulan saat ini:

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)

Bidang kunci:

Ladang Description
OpenConnections Total koneksi terbuka (sedang digunakan + menganggur).
InUse Koneksi saat ini diperiksa oleh penelepon.
Idle Koneksi menunggu di kolam renang.
WaitCount Berapa kali penelepon harus menunggu koneksi.
WaitDuration Total waktu tunggu kumulatif.

Jika WaitCount terus bertambah secara stabil, jangan tingkatkan MaxOpenConns secara otomatis. Pertama, verifikasi bahwa baris data, transaksi, dan koneksi terdedikasi ditutup dengan segera, lalu pastikan bahwa server dapat mendukung pool yang lebih besar.

SessionInitSQL

Gunakan SessionInitSQL untuk menjalankan pernyataan SQL pada setiap koneksi baru saat memasuki kumpulan. Fitur ini berguna untuk mengatur opsi tingkat sesi:

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)

Penyematan koneksi

Operasi tertentu menyematkan koneksi sehingga tidak dikembalikan ke kumpulan hingga operasi berakhir:

  • Transaksi (db.BeginTx) - Koneksi tetap terikat hingga Commit() atau Rollback() dipanggil.
  • Koneksi tunggal (db.Conn) - Koneksi tetap terikat hingga fungsi conn.Close() dipanggil.
  • Baris yang terbuka (db.QueryContext) - Koneksi tetap terikat hingga rows.Close() dipanggil.

Selalu tutup sumber daya ini segera untuk menghindari kelaparan kolam.

Mendeteksi dan mengatasi kelelahan kolam renang

Kelelahan kolam terjadi ketika semua koneksi sedang digunakan dan kolam telah mencapai MaxOpenConns. Penelepon baru memblokir hingga koneksi dikembalikan. Gejalanya termasuk latensi tinggi, penumpukan goroutine, dan kesalahan tenggat waktu konteks akhirnya.

Pantau kelelahan

Jajak pendapat db.Stats() secara berkala dan peringatkan saat perselisihan terdeteksi:

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

Penyebab Umum dan Solusi

Penyebab Gejala Solusi
MaxOpenConns terlalu rendah untuk beban kerja WaitCount tumbuh dengan mantap. Tingkatkan MaxOpenConns.
Baris tidak ditutup pada jalur penanganan kesalahan InUse tumbuh, Idle tetap di 0. Gunakan defer rows.Close() segera setelah QueryContext.
Transaksi jangka panjang InUse tetap tinggi. Jaga agar transaksi tetap singkat. Gunakan batas waktu konteks.
db.Conn digunakan secara tidak perlu InUse lebih tinggi dari yang diharapkan. Gunakan db.Conn hanya saat Anda memerlukan status lingkup sesi (tabel sementara).
MaxOpenConns Tidak diatur (tidak terbatas) Ratusan koneksi terbuka saat sistem terbebani. Selalu atur MaxOpenConns ke nilai terbatas.

Pemeriksaan kesehatan dan deteksi koneksi kedaluwarsa

Pool tidak secara aktif memvalidasi koneksi idle. Koneksi yang menganggur saat server mendaur ulangnya gagal pada penggunaan berikutnya. Konfigurasikan ConnMaxLifetime dan ConnMaxIdleTime agar merotasi koneksi sebelum koneksi tersebut menjadi usang:

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

Saat ConnMaxIdleTime menutup koneksi menganggur secara proaktif, log di SQL Server mungkin menunjukkan bahwa koneksi terputus. Ini adalah perilaku yang diharapkan, bukan kebocoran koneksi. Jika DBA Anda melaporkan penutupan koneksi yang tidak terduga, verifikasi bahwa ConnMaxIdleTime pengaturan tersebut selaras dengan ekspektasi pemantauan tim.

Jika aplikasi Anda terhubung melalui penyeimbang beban atau Azure SQL dengan replikasi geografis, atur ConnMaxLifetime ke 5 menit atau kurang. Pengaturan ini memastikan koneksi didistribusikan ulang di seluruh replika setelah failover.

Memvalidasi konektivitas saat startup

Selalu panggil db.PingContext setelah membuka database untuk mengonfirmasi string koneksi sudah benar dan server dapat dijangkau:

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

Mengekspor metrik kumpulan

Ekspos statistik kumpulan ke sistem pemantauan Anda dengan membaca db.Stats()secara berkala :

Contoh Prometheus

Daftarkan pengukur yang melacak statistik kumpulan dan memperbaruinya secara berkala:

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

Daftar periksa konfigurasi kumpulan

Area Recommendation
MaxOpenConns Selalu atur ke nilai terbatas. Sesuaikan dengan tingkat konkurensi beban kerja Anda, agar tetap di bawah batas koneksi server.
MaxIdleConns Tetapkan setidaknya separuh dari MaxOpenConns. Terlalu sedikit koneksi idle menyebabkan overhead akibat penyambungan ulang yang sering terjadi.
ConnMaxLifetime Atur ke 5 menit untuk Azure SQL atau lingkungan yang seimbang beban. Mencegah penumpukan koneksi yang kedaluwarsa.
ConnMaxIdleTime Atur ke 30-60 detik untuk menutup koneksi yang tidak lagi diperlukan.
Monitoring Lakukan polling pada db.Stats() dan buat peringatan untuk peningkatan WaitCount.
Pembersihan sumber daya Selalu defer rows.Close(), defer tx.Rollback(), dan defer conn.Close().
Validasi startup Hubungi db.PingContext setelahnya sql.Open untuk memverifikasi konektivitas.