Sterownik Microsoft go-mssqldb dla SQL Server

Sterownik go-mssqldb jest oficjalnym sterownikiem Microsoft Go dla Microsoft SQL. To czysta implementacja Go protokołu TDS (Tabular Data Stream), która korzysta ze standardowego database/sql interfejsu. Nie wymaga ODBC ani innych bibliotek C.

Sterownik łączy aplikacje Go ze wszystkimi obsługiwanymi wersjami SQL Server, Azure SQL Database, Azure SQL Managed Instance, bazą danych SQL w Fabric, Fabric Data Warehouse oraz Azure Synapse Analytics na Windows, Linux i macOS.

Wybieranie punktu początkowego

Plan bazowy produkcji dla Azure SQL

Użyj tego przykładu jako punktu wyjścia do produkcyjnego połączenia Azure SQL. Łączy tożsamość zarządzaną, jawne ustawienia TLS, ustawienia ograniczeń puli, limity czasu dla poszczególnych żądań, logikę ponawiania prób dla błędów przejściowych z losowym rozproszeniem opóźnień oraz strukturalne logowanie.

package main

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"log/slog"
	"math/rand"
	"net/url"
	"os"
	"time"

	mssql "github.com/microsoft/go-mssqldb"
	_ "github.com/microsoft/go-mssqldb/azuread"
)

const (
	maxAttempts    = 4
	baseBackoff    = 200 * time.Millisecond
	maxBackoff     = 3 * time.Second
	queryTimeout   = 2 * time.Second
	startupTimeoutDefault = 30 * time.Second
)

func main() {
	baseLogger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		Level: slog.LevelInfo,
	}))

	server, ok := getenvRequired("SQL_SERVER")
	if !ok {
		baseLogger.Error("invalid configuration", "error", "missing SQL_SERVER")
		os.Exit(1)
	}

	database, ok := getenvRequired("SQL_DATABASE")
	if !ok {
		baseLogger.Error("invalid configuration", "error", "missing SQL_DATABASE")
		os.Exit(1)
	}

	fedAuth := getenvOrDefault("SQL_FEDAUTH", "ActiveDirectoryManagedIdentity")
	appName := getenvOrDefault("SQL_APP_NAME", "go-mssqldb-example")
	startupTimeout := getenvDurationOrDefault("SQL_STARTUP_TIMEOUT", startupTimeoutDefault)
	logger := baseLogger.With(
		slog.String("server", server),
		slog.String("database", database),
		slog.String("fedauth", fedAuth),
		slog.String("appName", appName),
	)

	connString := buildConnString(server, database, appName, fedAuth)

	db, err := sql.Open("azuresql", connString)
	if err != nil {
		logger.Error("open database", "error", err)
		os.Exit(1)
	}
	defer db.Close()

	db.SetMaxOpenConns(20)
	db.SetMaxIdleConns(10)
	db.SetConnMaxIdleTime(2 * time.Minute)
	db.SetConnMaxLifetime(5 * time.Minute)

	rootCtx := context.Background()

	startupCtx, cancel := context.WithTimeout(rootCtx, startupTimeout)
	defer cancel()
	err = withRetry(startupCtx, logger, func(ctx context.Context) error {
		return db.PingContext(ctx)
	})
	if err != nil {
		logger.Error("ping database", "error", err)
		os.Exit(1)
	}

	var databaseName string
	err = withRetry(rootCtx, logger, func(ctx context.Context) error {
		queryCtx, cancel := context.WithTimeout(ctx, queryTimeout)
		defer cancel()

		return db.QueryRowContext(queryCtx, "SELECT DB_NAME()").Scan(&databaseName)
	})
	if err != nil {
		logger.Error("query database", "error", err)
		os.Exit(1)
	}

	logger.Info("database ready", "database", databaseName)
}

func withRetry(ctx context.Context, logger *slog.Logger, fn func(context.Context) error) error {
	var lastErr error

	for attempt := 1; attempt <= maxAttempts; attempt++ {
		lastErr = fn(ctx)
		if lastErr == nil {
			return nil
		}

		if !isTransient(lastErr) || attempt == maxAttempts {
			return lastErr
		}

		delay := backoffWithJitter(baseBackoff, maxBackoff, attempt)
		logger.WarnContext(ctx, "transient SQL error; retrying",
			"attempt", attempt,
			"delay", delay.String(),
			"error", lastErr,
		)

		timer := time.NewTimer(delay)
		select {
		case <-ctx.Done():
			timer.Stop()
			return ctx.Err()
		case <-timer.C:
		}
	}

	return lastErr
}

func backoffWithJitter(baseDelay, maxDelay time.Duration, attempt int) time.Duration {
	delay := baseDelay * time.Duration(1<<(attempt-1))
	if delay > maxDelay {
		delay = maxDelay
	}

	jitterFraction := 0.20
	multiplier := (1 - jitterFraction) + rand.Float64()*(2*jitterFraction)
	return time.Duration(float64(delay) * multiplier)
}

func buildConnString(server, database, appName, fedAuth string) string {
	query := url.Values{
		"database":               []string{database},
		"fedauth":                []string{fedAuth},
		"encrypt":                []string{"true"},
		"TrustServerCertificate": []string{"false"},
		"app name":               []string{appName},
		"log":                    []string{"1"},
	}

	return fmt.Sprintf("sqlserver://%s?%s", server, query.Encode())
}

func getenvRequired(key string) (string, bool) {
	value := os.Getenv(key)
	if value == "" {
		return "", false
	}
	return value, true
}

func getenvOrDefault(key, defaultValue string) string {
	value, ok := getenvRequired(key)
	if !ok {
		return defaultValue
	}
	return value
}

func getenvDurationOrDefault(key string, defaultValue time.Duration) time.Duration {
	value := os.Getenv(key)
	if value == "" {
		return defaultValue
	}

	parsed, err := time.ParseDuration(value)
	if err != nil {
		return defaultValue
	}

	return parsed
}

func isTransient(err error) bool {
	var sqlErr mssql.Error
	if !errors.As(err, &sqlErr) {
		return false
	}

	switch sqlErr.Number {
	case
		// Connection-establishment and transport transient errors.
		64, 233, 4060, 4221,
		10053, 10054,
		10928, 10929,

		// Azure SQL failover, throttling, and availability errors.
		40020, 40143, 40166,
		40197, 40501, 40540, 40613,
		42108, 42109,
		49918, 49919, 49920,

		// Common retryable statement-level contention errors.
		1205, 1222:
		return true
	default:
		return false
	}
}

Ten przykład wymaga zmiennych środowiskowych SQL_SERVER i SQL_DATABASE.

Opcjonalne zmienne środowiskowe:

  • SQL_FEDAUTH (domyślnie: ActiveDirectoryManagedIdentity)
  • SQL_APP_NAME (domyślnie: go-mssqldb-example)
  • SQL_STARTUP_TIMEOUT (domyślnie: 30s, analizowane przy użyciu time.ParseDuration)

Więcej informacji o każdej części tego przykładu można znaleźć w Azure SQL Database, puli połączeń, obsłudze błędów i wzorcach powtórek oraz logowaniu i diagnoscie.

Kluczowe funkcje

  • Pure Go: Nie wymaga CGo ani zewnętrznych zależności C.
  • Trzy formaty parametry połączenia: URL (sqlserver://), ADO (key=value) oraz ODBC (odbc:key=value).
  • Uwierzytelnianie Microsoft Entra ID: Wiele typów poświadczeń dostępnych za pośrednictwem pakietu azuread, w tym tożsamość zarządzana, jednostka usługi i tożsamość obciążenia roboczego.
  • SQL Server i Windows authentication: Obsługuje uwierzytelnianie SQL, NTLM, Kerberos oraz jednorazowe logowanie (SSO) na Windows.
  • Zawsze szyfrowane: szyfrowanie po stronie klienta z lokalnym certyfikatem, dostawcami kluczy Windows Certificate Store oraz Azure Key Vault.
  • Kopia masowa: Wysokowydajne operacje masowego wstawiania.
  • Parametry o wartości tabelarycznej (TVP): Przekazuj dane strukturyzowane do procedur przechowywanych.
  • Wiele protokołów: TCP, potoki nazwane, pamięć współdzielona i dedykowane połączenie administratora (DAC).
  • Szyfrowanie TDS 8.0: szyfrowanie end-to-end w trybie ścisłym.

Wprowadzenie

Artykuł Opis
Instalacja i wymagania systemowe Zainstaluj moduł sterownika i sprawdź środowisko Go.
Szybki start: Połącz się i zapytaj Połącz się z lokalną lub testową instancją SQL Server i uruchom pierwsze zapytanie w kilka minut.
Migracja z innych sterowników Migruj z lib/pq, pgx lub go-sql-driver/mysql do go-mssqldb.

Konfigurowanie połączeń

Artykuł Opis
Parametry połączenia Formaty parametrów połączenia URL, ADO i ODBC wraz z przykładami.
Opcje połączenia Limity czasu, rozmiar pakietu, przełączenie awaryjne, SessionInitSQL, oraz NewConnector.
Szyfrowanie i certyfikaty Tryby szyfrowania TLS, walidacja certyfikatów oraz TDS 8.0.
SQL Server i uwierzytelnianie systemu Windows Uwierzytelnianie SQL, NTLM, Kerberos i konfiguracja SSO.
Uwierzytelnianie identyfikatora Entra firmy Microsoft Opcje połączeń Microsoft Entra ID, przepływy poświadczeń i przykłady dostawców tokenów.
Najlepsze rozwiązania dotyczące zabezpieczeń Zapobieganie wtryskowi SQL, zarządzanie sekretami, szyfrowanie oraz najmniejsze uprawnienia.

Praca z danymi

Artykuł Opis
Mapowania typów danych Tabela konwersji typów z Go na SQL oraz typy właściwe dla sterowników.
Zapytania i oświadczenia Zapytania parametryzowane, Exec, Query, QueryRow, oraz wiele zbiorów wyników.
Transakcje Poziomy izolacji, punkty zapisu, wykrywanie martwego punktu i wzorce powtórek.
Obsługa błędów i wzorce powtórek Struktura błędów SQL Server, wykrywanie błędów przejściowych oraz wykładnicze cofnięcie.
Procedury składowane Parametry wyjściowe, ReturnStatus, oraz zbiory wyników z procedur.
Operacje zbiorcze Wysokowydajna wkładka zbiorcza z .CopyIn
Parametry wartości tabeli Przekaż dane strukturalne do procedur przechowywanych za pomocą mssql.TVP.
Dane JSON i XML Zapytaj, wstaw i przekształc dane JSON i XML za pomocą FOR JSON, OPENJSON, oraz FOR XML.
Zawsze szyfrowane Szyfrowanie po stronie klienta z lokalnymi certyfikatami, Windows Certificate Store oraz dostawcami Azure Key Vault.

Wdrażanie i obsługa

Artykuł Opis
Buforowanie połączeń Skonfiguruj pulę database/sql połączeń.
Programy współczesne Bezpieczeństwo gorutyny, pule pracowników, zapytania równoległe i eleganckie wyłączenie.
Dostrajanie wydajności Dostrajanie puli, rozmiar pakietów, przygotowane instrukcje i kopiowanie zbiorcze.
Azure SQL Database Uwierzytelnianie bezhasłowe, limity połączeń, dławienie i obsługa przełączania awaryjnego.
Troubleshooting Typowe błędy, konfiguracja logowania i diagnostyka certyfikatów.
Testowanie Wzorce testowania integracji i konfiguracja bazy testów.

Przewodniki po platformach i protokołach

Artykuł Opis
Linux i macOS Konfiguracja międzyplatformowa, Kerberos, NTLM i ścieżki certyfikacji.
Protokoły TCP, potoki nazwane, pamięć współdzielona, DAC oraz SQL Browser.
Rejestrowanie i diagnostyka Flagi logu, SetLogger, oraz SetContextLogger.

Prośba o funkcję

Aby zażądać funkcji, otwórz problem w repozytorium go-mssqldb GitHub.