Microsoft go-mssqldb SQL Server 用ドライバー

go-mssqldbドライバーはMicrosoft SQLの公式のMicrosoft Goドライバーです。 これはTDS(Tabular Data Stream)プロトコルの純粋なGo実装で、標準 database/sql インターフェースを使用しています。 ODBCや他のCライブラリは必要ありません。

このドライバーを使用すると、Windows、Linux、macOS 上の Go アプリケーションから、サポートされているすべてのバージョンの SQL Server、Azure SQL Database、Azure SQL Managed Instance、Fabric 内の SQL データベース、Fabric Data Warehouse、Azure Synapse Analytics に接続できます。

出発点を選択する

Azure SQLの運用ベースライン

このサンプルを本番向けAzure SQL接続の出発点として活用してください。 このソリューションは、マネージド ID、明示的な TLS 設定、バウンドプール設定、要求単位のタイムアウト、ジッターを適用した一時的な再試行ロジック、構造化されたログを組み合わせています。

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

このサンプルは SQL_SERVERSQL_DATABASE 環境変数を期待しています。

オプションの環境変数:

  • SQL_FEDAUTH (デフォルト: ActiveDirectoryManagedIdentity)
  • SQL_APP_NAME (デフォルト: go-mssqldb-example)
  • SQL_STARTUP_TIMEOUT (デフォルト: 30stime.ParseDurationと解析)

このサンプルの各部分の詳細については、「Azure SQL Database」「接続プーリング」「エラー処理と再試行パターン」ログおよび診断」をご覧ください。

主要な機能

  • Pure Go:CGoや外部C依存関係は不要です。
  • 3つの接続文字列形式:URL(sqlserver://)、ADO(key=value)、ODBC(odbc:key=value)。
  • Microsoft Entra ID認証:azureadパッケージを通じて管理型アイデンティティ、サービスプリンシパル、ワークロード識別など複数の認証情報タイプを利用できます。
  • SQL ServerおよびWindows 認証:Windows上でSQL認証、NTLM、Kerberos、シングルサインオン(SSO)をサポートします。
  • Always Encrypted:ローカル証明書、Windows証明書ストア、Azure Key Vault鍵提供者によるクライアント側暗号化。
  • 一括コピー: 高性能な大量挿入処理。
  • テーブル値パラメータ(TVP):構造化データをストアドプロシージャに渡します。
  • 複数のプロトコル:TCP、名前付きパイプ、共有メモリ、専用管理者接続(DAC)。
  • TDS 8.0暗号化:厳密モードを用いたエンドツーエンド暗号化。

概要

[アーティクル] Description
設置およびシステム要件 ドライバーモジュールをインストールし、Go環境を確認してください。
クイックスタート:接続とクエリ ローカルまたはテスト用のSQL Serverインスタンスに接続し、最初のクエリを数分で実行してください。
他のドライバーから移行 lib/pq、pgx、またはgo-sql-driver/mysqlからgo-mssqldbに移行してください。

接続の設定

[アーティクル] Description
接続文字列 URL、ADO、ODBCの接続文字列形式と例。
接続オプション タイムアウト、パケットサイズ、フェイルオーバー、 SessionInitSQLNewConnector
暗号化と証明書 TLS暗号化モード、証明書検証、TDS 8.0。
SQL Server 認証と Windows 認証 SQL認証、NTLM、Kerberos、SSOの設定。
Microsoft Entra ID 認証 Microsoft Entra IDの接続オプション、認証フロー、トークン提供者の例。
セキュリティのベスト プラクティス SQLインジェクション防止、秘密管理、暗号化、そして最小権限。

データの操作

[アーティクル] Description
データ型のマッピング Go から SQL への型変換テーブルとドライバー固有の型
クエリと文 パラメータ化されたクエリ、 ExecQueryQueryRow、そして複数の結果セット。
トランザクション 隔離レベル、セーブポイント、デッドロック検出、再試行パターンなどです。
エラー処理と再試行パターン SQL Serverエラー構造、一時的エラー検出、指数関数的バックオフ。
ストアド プロシージャ プロシージャからパラメータ、 ReturnStatus、結果セットを出力します。
一括操作 CopyIn を使用した高性能な一括挿入。
テーブル値パラメーター 構造化データを mssql.TVPでストアドプロシージャに渡します。
JSONおよびXMLデータ JSONおよびXMLデータを FOR JSONOPENJSONFOR XMLでクエリ、挿入、変換します。
常に暗号化 ローカル証明書、Windows証明書ストア、Azure Key Vaultプロバイダーによるクライアントサイド暗号化。

デプロイと運用

[アーティクル] Description
接続プール database/sql接続プールを設定してください。
並行プログラミング Goroutine の安全性、ワーカープール、並列クエリ、正常なシャットダウン。
パフォーマンスチューニング プールの調整、パケット サイズ、準備済みのステートメント、一括コピー。
Azure SQL Database パスワード不要認証、接続制限、スロットリング、フェイルオーバー処理。
Troubleshooting よくあるエラー、ログ設定、証明書診断。
テスト 統合テストパターンとテストデータベースの設定。

プラットフォームおよびプロトコルガイド

[アーティクル] Description
Linux と macOS クロスプラットフォーム設定、Kerberos、NTLM、証明書パス。
プロトコル TCP、名前付きパイプ、共有メモリ、DAC、SQLブラウザなどがあります。
ログと診断 ログフラグ、 SetLoggerSetContextLogger