Pilote go-mssqldb de Microsoft pour SQL Server

Le pilote go-mssqldb est le pilote Go officiel de Microsoft pour Microsoft SQL. C’est une implémentation pure Go du protocole TDS (Tabular Data Stream) qui utilise l’interface standard database/sql . Il ne nécessite pas ODBC ni d’autres bibliothèques C.

Le pilote connecte les applications Go à toutes les versions prises en charge de SQL Server, Azure SQL Database, Azure SQL Managed Instance, SQL dans Fabric, Fabric Data Warehouse, et Azure Synapse Analytics sur Windows, Linux et macOS.

Choisir votre point de départ

Base de référence de production pour Azure SQL

Utilisez cet exemple comme point de départ pour une connexion Azure SQL orientée production. Il combine l’identité gérée, des paramètres TLS explicites, des paramètres de pool à taille limitée, des délais d’attente définis au niveau de chaque requête, une logique de nouvelle tentative en cas d’erreur transitoire avec gigue aléatoire, et une journalisation structurée.

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

Cet échantillon nécessite les variables d’environnement SQL_SERVER et SQL_DATABASE.

Variables d’environnement optionnelles :

  • SQL_FEDAUTH (par défaut : ActiveDirectoryManagedIdentity)
  • SQL_APP_NAME (par défaut : go-mssqldb-example)
  • SQL_STARTUP_TIMEOUT (par défaut : 30s, analysé avec time.ParseDuration)

Pour plus d’informations sur chaque partie de cet exemple, voir Azure SQL Database, Connection Pooling, Gestion des erreurs et motifs de réessayage, ainsi que Journalisation et diagnostic.

Fonctionnalités clés

  • Pure Go : aucune dépendance à CGo ni à des bibliothèques C externes n’est requise.
  • Trois formats de chaîne de connexion : URL (sqlserver://), ADO (key=value), et ODBC (odbc:key=value).
  • Authentification Microsoft Entra ID : plusieurs types d’identifiants via le package azuread, y compris l’identité gérée, le principal de service et l’identité de charge de travail.
  • SQL Server et l’authentification Windows : Prend en charge l’authentification SQL, NTLM, Kerberos et l’authentification unique (SSO) sous Windows.
  • Toujours chiffré : chiffrement côté client avec des fournisseurs de clés de certificats locaux, Windows Certificate Store et Azure Key Vault.
  • Copie en masse : opérations d’insertion en masse hautes performances.
  • Paramètres à valeurs de table (TVP) : Transmettre les données structurées aux procédures stockées.
  • Protocoles multiples : TCP, pipes nommés, mémoire partagée et Dedicated Administrator Connection (DAC).
  • Chiffrement TDS 8.0 : Chiffrement de bout en bout avec mode strict.

Démarrage

Article Description
Installations et exigences système Installez le module pilote et vérifiez votre environnement Go.
Démarrage rapide : Connectez-vous et interrogez Connectez-vous à une instance locale ou testez SQL Server et lancez votre première requête en quelques minutes.
Migrer depuis d’autres pilotes Migrez de lib/pq, pgx ou go-sql-driver/mysql vers go-mssqldb.

Configurer des connexions

Article Description
Chaînes de connexion Formats de chaîne de connexion URL, ADO et ODBC avec des exemples.
Options de connexion Délais d’attente, taille du paquet, basculement, SessionInitSQL, et NewConnector.
Chiffrement et certificats Modes de chiffrement TLS, validation de certificats et TDS 8.0.
SQL Server et Authentification Windows Authentification SQL, NTLM, Kerberos et configuration SSO.
Authentification avec Microsoft Entra ID Options de connexion Microsoft Entra ID, flux d’identifiants et exemples de fournisseurs de tokens.
Bonnes pratiques de sécurité Prévention de l’injection SQL, gestion des secrets, chiffrement et moindre privilège.

Utiliser des données

Article Description
Mappages de types de données Table de conversion des types Go en types SQL et types propres au pilote.
Requêtes et énoncés requêtes paramétrées, Exec, Query, QueryRow, et plusieurs ensembles de résultats.
Transactions Niveaux d’isolement, points de sauvegarde, détection de blocage et schémas de réessayage.
Gestion des erreurs et motifs de réévaluation Structure d’erreur SQL Server, détection d’erreurs transitoires et reculement exponentiel.
procédures stockées Paramètres de sortie, ReturnStatus, et ensembles de résultats issus des procédures.
Opérations en bloc Insertion en bloc haute performance avec CopyIn.
Paramètres de type table Passer des données structurées vers des procédures stockées avec mssql.TVP.
Données JSON et XML Interrogez, insérez et transformez les données JSON et XML avec FOR JSON, OPENJSON, et FOR XML.
Toujours chiffré Chiffrement côté client avec des fournisseurs locaux de certificats, Windows Certificate Store et Azure Key Vault.

Déployer et exploiter

Article Description
Regroupement de connexions Configurez le pool de database/sql connexions.
Programmation simultanée Sécurité des goroutines, pools de workers, requêtes parallèles et arrêt normal.
Réglage des performances Réglage du pool, taille des paquets, instructions préparées et copie en bloc.
Base de données SQL Azure Authentification sans mot de passe, limites de connexion, limitation du débit et gestion du basculement.
Résolution des problèmes Erreurs courantes, configuration de journalisation et diagnostics de certificats.
Test en cours Patrons de test d’intégration et configuration de base de données de test.

Guides de plateforme et de protocole

Article Description
Linux et macOS Configuration multiplateforme, Kerberos, NTLM et chemins de certificat.
Protocoles TCP, pipes nommés, mémoire partagée, DAC et navigateur SQL.
Journalisation et diagnostics Consigner les indicateurs, SetLogger, et SetContextLogger.