将 go-mssqldb 与 Azure SQL 数据库配合使用

go-mssqldb驱动程序支持连接 Azure SQL 数据库、Azure SQL 托管实例 以及 Microsoft Fabric 中的 SQL 数据库。 本文涵盖了Azure特有的配置、认证、连接限制以及与本地SQL Server不同的故障排除。

连接到 Azure SQL 数据库

Azure SQL 数据库 默认需要加密连接。 明确指定 encrypt=trueTrustServerCertificate=false 以便连接使用 TLS 并验证服务器证书:

db, err := sql.Open("sqlserver",
    "sqlserver://<user>:<password>@<server>.database.windows.net?database=<database>&encrypt=true&TrustServerCertificate=false")
if err != nil {
    panic(err)
}

Note

如果你省略encrypt了 ,驱动不会自动添加 Azure 特定的 TLS 设置。 在 Azure SQL 连接字符串中保留 encrypt=true&TrustServerCertificate=false

Microsoft Entra ID认证会消除连接字符串中的密码。 ActiveDirectoryDefault 自动选择最适合环境的可用凭证,使开发变得更方便:

import (
    "database/sql"
    "log"

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

func main() {
    db, err := sql.Open("azuresql",
        "sqlserver://<server>.database.windows.net?database=<database>&fedauth=ActiveDirectoryDefault&encrypt=true&TrustServerCertificate=false")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
}

Important

ActiveDirectoryDefault 这对开发很方便,但由于它会探测多个凭证来源,可能会增加连接延迟。 对于生产服务,偏好显式方法,如 ActiveDirectoryManagedIdentityActiveDirectoryServicePrincipal

ActiveDirectoryDefault 如何解析凭证

ActiveDirectoryDefault 按顺序尝试以下凭据源,并使用第一个成功的凭据源:

Order 凭证来源 典型环境
1 环境变量 (AZURE_CLIENT_IDAZURE_TENANT_IDAZURE_CLIENT_SECRET CI/CD 管道,Docker 容器
2 工作负载标识 具有 Azure Workload Identity 的 Kubernetes Pod
3 托管标识 Azure VMs, App Service, Container Apps, Azure Functions
4 Azure CLI (az login 地方发展
5 Azure开发人员 CLI (azd auth login 地方发展

这种凭证链在开发过程中很 ActiveDirectoryDefault 方便,但顺序探测会给每次新连接增加延迟。 对于生产环境,指定精确的认证方法(例如 ActiveDirectoryManagedIdentity),这样驱动程序可以跳过不必要的检查。

托管在 Azure 中的应用(App Service、Container Apps、Azure Functions 或 Azure VMs)应使用带有明确fedauth值的管理身份。 这种方法避免了凭证链的开销,并消除了对环境变量或CLI状态的任何依赖。

系统分配的托管标识:

sqlserver://<server>.database.windows.net?database=<database>&fedauth=ActiveDirectoryManagedIdentity&encrypt=true&TrustServerCertificate=false

用户分配的管理身份 (指定客户端ID):

sqlserver://<server>.database.windows.net?database=<database>&fedauth=ActiveDirectoryManagedIdentity&user id=<client-id>&encrypt=true&TrustServerCertificate=false

在数据库中授予身份访问权

在配置 Azure 资源的管理身份后,创建一个包含的数据库用户:

CREATE USER [my-app-identity] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [my-app-identity];
ALTER ROLE db_datawriter ADD MEMBER [my-app-identity];

对于系统分配标识,请使用 Azure 资源名称。 对于用户分配的身份,使用身份名。

用于自动化的服务主体

对于CI/CD流水线或服务间认证:

sqlserver://<server>.database.windows.net?database=<database>&fedauth=ActiveDirectoryServicePrincipal&user id=<client-id>&password=<client-secret>&encrypt=true&TrustServerCertificate=false

关于所有凭证类型,请参见 Microsoft Entra ID 认证

配置Azure防火墙

Azure SQL 数据库 使用服务器级防火墙。 你必须允许客户端的公共IP地址,或者使用私有端点。

错误:无法打开服务器

此错误提示表明 Azure 防火墙正在阻挡您的客户端 IP 地址:

mssql: login error: Cannot open server '<server>' requested by the login.
Client with IP address '<client-ip>' is not allowed to access the server.

解决方案:

  1. 在 Azure 门户中添加防火墙规则:SQL 服务器>网络>添加防火墙规则
  2. 如果您的应用运行在 Azure 中,请启用允许 Azure 服务和资源访问该服务器
  3. 对于私有连接,配置一个 私有端点

错误:连接超时

如果连接超时且未出现明确的错误提示,则防火墙很可能在静默阻止该连接。 先验证防火墙规则。

按服务层划分的连接限制

Azure SQL 数据库 根据服务层级为每个数据库强制执行连接限制。 超过限制会导致新连接的认证失败。 完整的限制表请参见 DTU单数据库资源限制vCore单数据库资源限制

设置MaxOpenConns以匹配你的等级

始终将 MaxOpenConns 设置为低于你的 Azure SQL 层级连接限制的值:

// Example for S2 tier (60 max workers).
// Leave headroom for Azure management connections and other clients.
db.SetMaxOpenConns(20)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5 * time.Minute)

Tip

如果多个应用程序共享同一数据库,则将连接限制分配到所有应用程序之间。 例如,如果三个服务共享一个S2数据库(最多60名工作者),每个服务分配15-20个连接。

处理 Azure SQL 限制

当数据库接近资源限制(CPU、IO、内存或会话数量)时,Azure SQL 数据库可以限制连接和查询。 限速表现为特定的错误编号。

常见的节流错误

错误号 消息模式 原因
10928 Resource ID: %d. The %s limit for the database is %d and has been reached. 会话或工作线程已达上限。
10929 Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d. 资源调速器限流。
40501 The service is currently busy. 通用限流。 请重试。
40544 The database has reached its size quota. 数据库大小已达上限。 在重试前先增加容量或剩余空间。
40549 Session is terminated because you have a long-running transaction. 交易已超过时间限制。
40550 Session is terminated because of too many locks. 过度获取锁。
40551 Session is terminated because of excessive tempdb usage. 过度使用tempdb。
40552 Session is terminated because of excessive transaction log usage. 事务日志空间已超限。
40553 Session is terminated because of excessive memory usage. 过度消耗内存。
40613 Database '%.*ls' on server '%.*ls' is not currently available. 数据库正在移动或重新配置。
49918 Cannot process request. Not enough resources to process request. 资源耗尽。
49919 Cannot process create or update request. 太多并发的创建/更新操作。
49920 Cannot process request. Too many operations in progress. 已达到并发操作上限。

重试受限制的请求

上表中的大多数 Azure SQL 限制错误和可用性错误都是暂时性错误,应采用指数退避方式重试。 错误 40544 不是瞬态的。 这意味着数据库已达到其大小配额,因此,在扩展数据库或删除数据之前,该操作将无法成功完成。

完整的重试实现,请参见 错误处理和重试模式

import (
    "errors"

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

func isAzureThrottling(err error) bool {
    var mssqlErr mssql.Error
    if !errors.As(err, &mssqlErr) {
        return false
    }
    switch mssqlErr.Number {
    case 10928, 10929, 40501, 40549, 40550, 40551, 40552, 40553,
        40613, 49918, 49919, 49920:
        return true
    }
    return false
}

连接复原能力

Azure SQL 数据库 偶尔会重新配置服务器以进行更新、故障转移和负载均衡。 这些事件会断开现有连接,表现为 driver: bad connection 错误。 将池配置为自动恢复:

db.SetConnMaxLifetime(5 * time.Minute)  // Rotate connections so stale ones are replaced.
db.SetConnMaxIdleTime(2 * time.Minute)  // Recycle before Azure gateway drops idle connections (30 min).
db.SetMaxIdleConns(10)                  // Keep warm connections for quick recovery.

Note

Azure SQL 的网关会关闭处于空闲状态约 30 分钟的连接。 将 ConnMaxIdleTime 设置为远低于该阈值,以避免在空闲期后的首次查询时出现 driver: bad connection 错误。 对于非事务调用, database/sql 在新连接时会自动重试。 对于事务调用,你的代码必须捕捉错误并重试整个事务。

故障切换后重新连接

在事务之外,当驱动程序将连接标记为不可用时,database/sql 可以透明地重试在不良连接上发起的调用。 这种行为并不是针对限速、故障切换或其他可重试SQL错误的完整瞬态故障重试策略。 将数据库调用封装在重试函数中,以处理这些情况:

var count int
err := RetryFunc(ctx, DefaultRetryConfig, func(ctx context.Context) error {
    return db.QueryRowContext(ctx, "SELECT COUNT(*) FROM HumanResources.Employee").Scan(&count)
})

关于实现,请参见RetryFunc

Azure SQL 托管实例

Azure SQL 托管实例 支持与本地 SQL Server 相同的驱动功能,但有一些不同之处:

功能 Azure SQL 数据库 Azure SQL 托管实例
SQL Server 代理 不可用 可用
跨数据库查询 不可用 可用
链接的服务器 不可用 可用
命名管道 不可用 不可用(仅限TCP)
共享内存 不可用 不可用(仅限TCP)
Windows 身份验证(SSPI) 不可用 在托管的VNet中提供

连接到托管实例:

sqlserver://<user>:<password>@<instance>.database.windows.net?database=<database>&encrypt=true&TrustServerCertificate=false

Microsoft Fabric 中的 SQL 数据库

Important

Fabric 中的 SQL 数据库需要 Microsoft Entra ID 认证。 不支持SQL Server认证。

对于生产工作负载,应优先选择显式的 fedauth 模式,而不是 ActiveDirectoryDefault,以避免在建立新连接时产生凭证链探测开销。

Fabric 中的 SQL 数据库支持使用 Microsoft Entra ID 身份验证的 go-mssqldb驱动程序:

db, err := sql.Open("azuresql",
    "sqlserver://<server>.database.fabric.microsoft.com?database=<database>&fedauth=ActiveDirectoryDefault&encrypt=true&TrustServerCertificate=false")
if err != nil {
    panic(err)
}

Azure SQL 性能提示

Tip 详细信息
使用连接池 Azure SQL 会将每个打开的连接计入tier limit。 保持 MaxOpenConns 受限。
启用 encrypt=strict 为了获得最强的安全性,可以使用TDS 8.0加密: encrypt=strict。 Azure SQL 数据库 支持严格模式。
使用 ApplicationIntent=ReadOnly 将读取密集型查询路由到只读副本:ApplicationIntent=ReadOnly 适用于 Premium、业务关键和超大规模层级。
监控DTU/vCore使用情况 CPU、IO 或工作进程使用率较高表明你的层级规格可能偏低。 使用Azure Monitor来跟踪资源利用率。
保持交易简短 Azure SQL 会终止超出资源阈值的会话(错误 40549)。
使用区域终结点 把你的应用放在与数据库相同的Azure区域,以尽量减少延迟。

Azure SQL 故障排查清单

症状 可能的原因 解决方案
Cannot open server 防火墙规则缺失 添加你的 IP 或启用 Azure 服务访问。
Login failed 错误的凭证或数据库用户缺失 确认登录是否存在且有数据库访问权限。
连接会间歇性超时 服务器重新配置或故障切换 实现重试逻辑和连接轮换。
Resource limit reached 太多并发连接 降低 MaxOpenConns 并及时关闭连接。
The service is currently busy Azure SQL throttling 使用指数退避重试。 考虑扩大规模。
正常运行后查询速度变慢 DTU/vCore 用尽 查看 Azure Monitor 的指标。 扩大或优化查询。