本文为常见错误和与驱动程序的连接问题 go-mssqldb 提供了解决方案。
从最简单的检查开始
在启用详细日志记录或更改池设置之前,请先逐项核对以下清单:
- 验证基本可达性:服务器名称、端口、防火墙规则,以及是否接受SQL Server或Azure SQL连接。
- 验证认证输入:驱动程序名称、用户名、密码、域名格式或
fedauth配置。 - 核实TLS设置:
encrypt、证书路径、hostnameincertificate以及是否TrustServerCertificate适合环境。 - 只有在连接设置正确后,才会调查池耗尽、连接过期、重试逻辑以及查询诊断慢或阻塞的情况。
请参阅本文前面几节,了解连接建立失败问题。 只有在连接至少有时能够成功、但随后在负载较高时、空闲一段时间后或故障切换期间失败时,才使用后续部分。
连接错误
以下章节将介绍常见的连接相关错误信息及其解决方案。
无法打开TCP连接
错误消息:unable to open tcp connection with host 'localhost:1433': dial tcp 127.0.0.1:1433: connectex: No connection could be made because the target machine actively refused it.
原因和解决方法:
- SQL Server 未运行。 启动 SQL Server 服务。
- TCP/IP 没有启用。 打开 SQL Server 配置管理器,并在 SQL Server 网络配置>协议下启用 TCP/IP。
- 换错端口了。 在 SQL Server 配置管理器中验证端口,或者对于命名实例,使用 SQL Server Browser。
- 防火墙阻挡了端口。 为端口1433(或你配置的端口)添加一个入站规则。
用户登录失败
错误消息:mssql: login error: Login failed for user '<user>'.
原因和解决方法:
- 用户名或密码错误。 核实凭证。
- SQL Server 认证已被禁用。 在服务器属性中启用 SQL Server 和 Windows 认证模式。
- 该登录账号不存在。 在 SQL Server 创建登录。
- 登录用户无法访问目标数据库。 使用
CREATE USER授予数据库访问权限。
证书验证错误
错误消息:TLS Handshake failed: x509: certificate signed by unknown authority
原因和解决方法:
- 服务器使用自签名证书。 提供带有
certificateorserverCertificate参数的证书路径,或仅设置TrustServerCertificate=true用于开发。 - CA证书不在系统信任存储中。 将CA证书添加到操作系统信任存储中,或用
certificate参数指定。 - 主机名不匹配。 使用
hostnameincertificate来指定证书中预期的名称。
更多信息请参见 加密与证书。
超过连接超时时间
错误消息:unable to open tcp connection with host '<server>:1433': dial tcp: i/o timeout
原因和解决方法:
- 网络连接问题。 使用
telnet <server> 1433或Test-NetConnection -ComputerName <server> -Port 1433验证你可以连接到服务器。 - DNS解析失败。 确认主机名是否正确解析。
- 增加
dial timeout或connection timeout在连接字符串中。
身份验证错误
以下章节介绍认证错误信息。
NTLM认证失败
错误消息:NTLM authentication failed
原因和解决方法:
- 域名格式错误。 在
user id参数中使用DOMAIN\user。 在URL格式中,将反斜杠编码为%5C。 - 密码错了。 验证域名密码。
Kerberos 身份验证失败
错误消息:krb5: cannot resolve KDC for realm
原因和解决方法:
- 缺少
/etc/krb5.conf或其配置错误。 请确认该[realms]部分包含了你域名的正确KDC地址。 - 没有有效机票。 运行
klist以检查是否有有效票证,或运行kinit以获取一个票证。 - 找不到Keytab文件。 验证参数中的
krb5-keytabfile路径。
更多信息请参见SQL Server和Windows 身份验证。
Microsoft Entra ID 认证失败
错误信息: clientCredentialFromCert: error reading certificate: ... 或 DefaultAzureCredential: failed to acquire a token
原因和解决方法:
- 客户ID、租户ID或客户秘密信息错误。 验证连接字符串或环境变量中的值。
- 托管身份没有在主机上配置。 在 Azure 门户中验证身份。
- 缺少
azuread包导入。 导入github.com/microsoft/go-mssqldb/azuread并使用azuresql驱动名。
有关详细信息,请参阅 Microsoft Entra ID 身份验证。
用户登录失败 ''(空用户名)
错误消息:mssql: login error: Login failed for user ''.
原因:你将 sql.Open("sqlserver", ...) 与 fedauth 参数一起使用。 Entra ID 身份验证需要由 azuread 包注册的 azuresql 驱动程序名称。 使用标准 sqlserver 驱动程序时, fedauth 参数会被忽略,驱动程序尝试在无用户名的情况下进行SQL认证。
解决方案:导入 azuread 包并使用 azuresql 驱动名:
import _ "github.com/microsoft/go-mssqldb/azuread"
db, err := sql.Open("azuresql",
"sqlserver://<server>.database.windows.net?database=AdventureWorks2025&fedauth=ActiveDirectoryDefault&encrypt=true&TrustServerCertificate=false")
if err != nil {
panic(err)
}
有关详细信息,请参阅 Microsoft Entra ID 身份验证。
查询错误
以下章节涵盖查询执行错误信息。
不支持 LastInsertID
错误消息:LastInsertId is not supported. Please use the OUTPUT clause or add 'select ID = convert(bigint, SCOPE_IDENTITY())' to the end of your query.
解决方案: go-mssqldb 驱动不支持 LastInsertId()。 分别使用 OUTPUT 子句或查询 SCOPE_IDENTITY()。
临时表未找到
错误消息:mssql: Invalid object name '#TempTable'.
原因:临时表是按连接设置的。 如果你在一次调用中创建临时表,而在另一次调用中查询它,这两次调用可能会使用连接池中的不同连接。
解决方案:使用 db.Conn(ctx) 将其固定到单个连接,或将操作封装在事务中。
有关详细信息,请参阅 存储过程。
Azure SQL 错误
以下章节将介绍针对Azure SQL 数据库的具体错误。
瞬态连接错误编号
以下共享列表可作为符合有界重试条件的瞬态连接建立错误和请求路径中的传输失败的参考:
在建立连接期间发生或向服务器发送请求时,以下错误是暂时性的。 在短的、受限的退避时重试。 在几次重试之后保留的错误通常表示配置问题(服务器错误、权限缺失、配额耗尽),重试无法解决。
| Error | Message | Troubleshooting |
|---|---|---|
64 |
A connection was successfully established with the server, but then an error occurred during the login process. (provider: TCP Provider, error: 0 - The specified network name is no longer available.) |
TCP 连接在握手过程中中断。 并非凭据错误。 如果问题仍然存在,请检查是否存在客户端网络不稳定的情况,或是否有会丢弃半建立连接的中间设备。 |
233 |
The client was unable to establish a connection because of an error during connection initialization process before login. |
预登录传输或 TLS 失败。 服务器通常会在无法接受该连接时返回该响应(例如资源耗尽、已达到最大连接数,或客户端不受支持)。 并非凭据错误。 验证服务器运行状况,然后检查客户端登录超时、TLS 设置和客户端/服务器 TLS 版本兼容性。 |
4060 |
Cannot open database "%.*ls" requested by the login. The login failed. |
登录已通过身份验证,但无法打开所请求的数据库。 暂时原因包括数据库处于转换过程中(故障转移、还原、扩缩容)或已自动暂停。 永久性原因(数据库不存在,登录缺少访问权限)不会通过重试来修复;检查数据库名称、登录映射和数据库状态。 |
4221 |
Login to read-secondary failed due to long wait on 'HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING'. |
该副本无法用于登录访问,因为在回收该副本时,当时正在进行中的事务缺少行版本。 回滚或提交主节点上的活动事务以解决该问题。 通过避免主数据库上的长时间写入事务来缓解此问题。 |
10053 |
A transport-level error has occurred when sending the request to the server. (provider: TCP Provider, error: 0 - An established connection was aborted by the software in your host machine.) |
本地端中止连接。 检查客户端网络运行状况以及任何本地防火墙或 VPN 客户端。 |
10054 |
A transport-level error has occurred when sending the request to the server. (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.) |
远程端发送 TCP 重置。 常见原因包括:对端进程崩溃、防火墙注入了重置包,或 Azure SQL 网关关闭了空闲连接。 对于空闲重置这类情况,请在客户端启用 TCP 保活,或缩短连接池的空闲超时时间。 |
10928 |
Resource ID: %d. The %s limit for the database is %d and has been reached. See 'http://go.microsoft.com/fwlink/?LinkId=267637' for assistance. |
数据库超过Azure SQL资源治理限制。 资源 ID 1 表示工作线程限制;资源 ID 2 表示会话限制。 从消息中识别限制类型,然后降低并发、扩容数据库,或缩短长时间占用该资源的操作的执行时间。 |
10929 |
Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d, and the current usage for the database is %d. However, the server is currently too busy to support requests greater than %d for this database. |
数据库已超过其最低保障值,底层服务器正在对其进行限流。 当邻居负载下降时,重试通常成功。 持续出现表示需要更高的服务层级或不太干扰的环境。 |
40020、40143、40166、40540 |
在故障转移期间,于错误 40197 的 Error code %d 槽中报告。 |
嵌入在 40197 故障转移消息中的子代码,在某些路径中会显示为顶层错误代码。 将它们视为 40197。 |
40197 |
The service has encountered an error processing your request. Please try again. Error code %d. |
Azure SQL 中的一次软件升级、硬件故障或其他故障转移事件。 重新连接会将你路由到一个健康的副本。 嵌入的错误代码标识故障转移类型。 如果错误仍然存在,请捕获会话跟踪 ID 并联系支持人员。 |
40501 |
The service is currently busy. Retry the request after 10 seconds. Incident ID: %ls. Code: %d. |
Azure SQL 引擎限流。 建议的最低退避时间为 10 秒。 持续受限流表明工作负载已超过数据库的资源分配;请提升服务层级或减少并发。 |
40613 |
Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later. If the problem persists, contact customer support, and provide them with the session tracing ID of '%.*ls'. |
数据库不可用,通常发生在故障转移过程中,或在缩放操作期间短暂出现。 按退避策略重试;如果问题持续几分钟以上,请记录会话跟踪 ID 并创建支持工单。 |
42108 |
Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. |
专用 SQL 池(Synapse)处于暂停状态。 只有在池恢复后,重试才会成功。 显式地恢复池,或将工作负载安排在池恢复后运行。 |
42109 |
The SQL pool is warming up. Please try again. |
专用 SQL 池正在恢复。 按退避策略重试,直到池上线;预热通常需要几分钟。 |
49918 |
Cannot process request. Not enough resources to process request. The service is currently busy. Please retry the request later. |
服务器当前无法分配足够的资源来满足请求。 在退避时重试。 如果错误仍然存在,请纵向扩展数据库或弹性池。 |
49919 |
Cannot process create or update request. Too many create or update operations in progress for subscription "%ld". |
管理操作的订阅级并发限制。 减少并行创建/更新调用或将它们错开。 |
49920 |
Cannot process request. Too many operations in progress for subscription "%ld". |
针对正在进行的操作的订阅级并发限制。 降低并行度,或等待当前正在进行的操作完成。 |
语句级错误不在此列表中,因为它们发生在连接建立之后,而且即使失败,会话仍可继续使用。 最常见的可重试语句错误是 1205(死锁受害者)和 1222(锁请求超时)。 重试整个事务,而不是单个失败语句。
错误消息文本来自Azure SQL暂时性连接错误。 各个驱动程序维护各自的内置重试列表;此目录说明了在 SQL Server、Azure SQL 数据库、Azure SQL 托管实例、Microsoft Fabric 中的 SQL 数据库以及 Azure Synapse Analytics 中的专用 SQL 池中,哪些错误符合重试条件。
无法打开服务器(防火墙)
错误消息:mssql: login error: Cannot open server '<server>' requested by the login. Client with IP address '203.0.113.42' is not allowed to access the server.
原因和解决方法:
- 你的客户端IP不在Azure SQL防火墙规则中。 在 Azure 门户中添加防火墙规则:SQL 服务器>网络>添加防火墙规则。
- 如果你的应用运行在 Azure 中,启用允许 Azure 服务和资源访问该服务器。
- 对于私有连接,配置一个私有端点。
达到资源限制
错误消息:mssql: Resource ID: 1. The session limit for the database is 300 and has been reached.
原因和解决方法:
- Azure SQL 层的并发连接太多了。 在池配置中降低
MaxOpenConns。 - 连接泄漏(未关闭的结果集或事务)。 检查是否缺少
defer rows.Close()或defer tx.Rollback()调用。 - 多个应用程序共享数据库。 将连接限制分配到所有客户端。
关于按层划分的Azure SQL连接限制,请参见Azure SQL 数据库。
服务当前繁忙(限流中)
错误消息:mssql: The service is currently busy. Retry the request after 10 seconds. Code: 40501.
原因和解决方法:
- 数据库负载沉重。 使用指数退避实现重试逻辑。
- 工作负载超过了该层的DTU或vCore容量。 考虑扩大规模。
关于重试实现模式,请参见 错误处理和重试模式。
数据库目前不可用
错误消息:mssql: Database 'AdventureWorks2025' on server '<server>' is not currently available. Code: 40613.
原因:Azure SQL 正在重新配置数据库(故障切换、更新或扩展操作)。 该状态为暂态错误。
解决方案:重新尝试操作。 数据库通常在几秒钟内即可访问。 更多信息请参见 错误处理和重试模式。
连接不良错误
错误 driver: bad connection 表示驱动程序检测到已有连接无法使用。 对于非事务性调用,database/sql 池会自动在新的连接上重试该操作,但活动事务中的操作会立即失败。
如果应用从未成功连接,千万别从这部分开始。
driver: bad connection 通常表明,在初始连接已正常建立并工作后,发生了连接复用、故障切换、空闲超时或网络中断。
常见原因
| 原因 | 典型方案 | 修复 |
|---|---|---|
| Azure SQL 网关空闲超时 | 连接在 Azure 网关后方空闲了 30 多分钟。 | 将 db.SetConnMaxIdleTime(2 * time.Minute) 设置为在网关关闭空闲连接之前回收这些连接。 |
| 网络中断 | 客户端与服务器之间的网络暂时故障。 | 为非事务操作实现重试逻辑。 参见错误处理。 |
| 服务器端会话终止 | DBA终止了会话,或者服务器被重启了。 | 请重试。 将 db.SetConnMaxLifetime 设置为轮换连接。 |
| Azure SQL 重新配置 | 故障切换、扩展或补丁事件导致连接中断。 | 将 ConnMaxLifetime 设置为 5 分钟或更短。 实现重试逻辑。 |
| 长时间运行的事务超时 | Azure SQL 终止了会话(错误 40549)。 | 保持交易简短。 将大型操作分解成较小的批处理。 |
数据库/SQL如何处理不良连接
对于事务之外的调用(db.QueryContext、db.ExecContext),当驱动程序报告连接已损坏时,database/sql 池会自动在新连接上重试该操作。 这次重试对你的代码是透明的。
对于交易(tx.QueryContext, tx.ExecContext)内的调用,池无法重试,因为交易状态丢失。 你的代码必须捕捉错误,回滚并重试整个交易。
Azure SQL 的推荐池设置
配置池以应对 Azure 网关超时和故障切换:
db.SetConnMaxLifetime(5 * time.Minute) // Rotate connections to recover from failovers.
db.SetConnMaxIdleTime(2 * time.Minute) // Recycle before Azure gateway drops idle connections (30 min).
db.SetMaxIdleConns(10) // Keep warm connections for quick recovery.
db.SetMaxOpenConns(20) // Stay below your tier's connection limit.
对于本地部署的 SQL Server,ConnMaxIdleTime没那么关键,因为不存在网关空闲超时。 不过,启用该设置可以防止在网络中断后出现陈旧连接。
有关详细配置指南,请参见 Azure SQL 数据库。
泳池耗尽
连接池耗尽是指池中的所有连接均被占用,而新的调用方会被阻塞,等待可用连接。
症状
- 请求在负载下会变慢或超时。
-
db.Stats().WaitCount持续增长。 -
db.Stats().InUse等于MaxOpenConns。 - 在高峰时段,上下文截止时间超过了错误。
诊断
在您的应用中添加泳池监控:
stats := db.Stats()
log.Printf("Pool: open=%d inUse=%d idle=%d waitCount=%d waitDuration=%v",
stats.OpenConnections, stats.InUse, stats.Idle,
stats.WaitCount, stats.WaitDuration)
常见原因和解决方案
| 原因 | 如何识别 | 修复 |
|---|---|---|
rows.Close() 未调用 |
InUse 随着时间增长,从不减少。 |
在每个 QueryContext 后添加 defer rows.Close()。 |
| 长时间运行的事务 |
InUse 在批处理期间维持在较高水平。 |
保持交易简短。 将大批量任务拆分成较小的批次进行处理。 |
MaxOpenConns 太低了 |
WaitCount 在排除被固定的资源和泄漏后,在正常负载下仍会持续增长。 |
增大 MaxOpenConns。 |
MaxOpenConns 未设置 |
峰值负载下有数百个打开的连接。 | 将 MaxOpenConns 设置为有界值。 |
调用 db.Conn 时发生 Goroutine 泄漏 |
InUse 增长,而请求量并未相应增长。 |
确保每个 db.Conn() 结果都以 defer conn.Close()闭合。 |
有关连接池配置的详细指导,请参见 Connection pooling。
查询诊断缓慢或阻塞
设置查询超时
利用上下文截止时间识别慢查询,防止被阻拦的SQL调用钉住连接和延迟调用者:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
rows, err := db.QueryContext(ctx, "SELECT * FROM LargeTable WHERE Status = @s",
sql.Named("s", "active"))
if err != nil {
// Check if the error was a timeout.
if ctx.Err() == context.DeadlineExceeded {
log.Println("Query exceeded 5-second timeout")
}
return err
}
defer rows.Close()
关于完整的性能调查工作流程,包括查询存储、DMV、缺失索引分析和基准测试,请参见性能调优。
死锁诊断
错误消息:mssql: Transaction (Process ID 52) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
错误编号:1205
解决方案:死锁发生在并发系统中。 为错误1205实现自动重试逻辑。 有关死锁重试封装函数,请参见 事务。
预防策略:
- 在所有查询中按相同顺序访问表。
- 保持交易时间简短,避免交易过程中的用户互动。
- 使用
READ COMMITTED SNAPSHOT隔离来减少锁争用。
同一查询中反复出现死锁表示存在设计问题。 使用死锁图(通过扩展事件或系统健康会话捕获)来识别竞争语句和锁类型。 完整攻略请参见 死锁攻略。 关于 Go 中的死锁处理策略,请参见 死锁处理 和 处理死锁。
容器相关的证书错误(Go 1.23及以后版本)
错误消息:x509: negative serial number
原因:Go 1.23严格执行RFC 5280。 SQL Server 在 Docker 容器中生成的自签名证书使用负序列号,Go 会拒绝该序列号。
解决方法:
- 对于测试环境,可以添加
TrustServerCertificate=true跳过证书验证,或者encrypt=disable完全关闭加密功能。 - 对于CI/CD,设置
GODEBUG=x509negativeserial=1环境变量恢复Go 1.23之前的行为,但不更改连接字符串。 - 在
go.mod(Go 1.23 及更高版本)中,添加godebug x509negativeserial=1指令,以在构建时应用该覆盖设置。
注意
不要使用 TrustServerCertificate=true 或 encrypt=disable 投入生产。 这些选项会禁用安全检查。 生产时,使用经过正确签名的证书。
SHA-1证书错误(Go 1.24及更高版本)
错误提示:tls: handshake failure或TLS Handshake failed: EOF连接较旧的 SQL Server 实例时。
原因:Go 1.24默认禁止在TLS证书中使用SHA-1签名算法。 较旧的 SQL Server 版本和部分本地安装使用通过 SHA-1 签名的证书。
解决方法:
- 重新签发SHA-256或更高版本的服务器证书(推荐)。
- 设置
GODEBUG=tlssha1=1环境变量暂时重新启用 SHA-1 支持。 - 在
go.mod(Go 1.23及更高版本)中,添加指令godebug tlssha1=1。
何时使用 encrypt=disable 与 TrustServerCertificate=true
| 设置 | 它的作用是什么 | 何时使用 |
|---|---|---|
TrustServerCertificate=true |
它加密流量,但跳过证书验证。 | 本地开发和测试,服务器使用自签名证书。 |
encrypt=disable |
以明文传输流量(未使用 TLS)。 | 无法使用 TLS 的遗留环境。 不建议这样做。 |
encrypt=strict |
TDS 8.0,从第一个字节开始就实现了完整的TLS验证。 | SQL Server 2022 或 Azure SQL 上的生产环境 |
编码与排序问题
隐式转换警告
如果将 string 参数(作为 nvarchar 发送)传递给 varchar 列,SQL Server 会执行隐式转换,这可能导致无法使用索引。
本例延续了本文前面示例片段中的 database/sql 和 mssql 设置。
解决方案:对varchar列使用mssql.VarChar:
db.QueryContext(ctx, "SELECT * FROM Production.Product WHERE ProductNumber = @p1",
mssql.VarChar("FR-R92B-58"))
CharsetToUTF8 处理非拉丁字符时出错
错误信息:在查询 varchar 中包含中文、日文或其他非拉丁字符且使用诸如 SQL_Latin1_General_CP1_CI_AS 之类排序规则的列时,出现 CharsetToUTF8: ...。
原因:驱动程序尝试将该列代码页转换为 UTF-8,但存储的字节与排序预期编码不符。
解决方法:
- 对于存储非拉丁文本的列,请使用
nvarchar代替varchar。nvarchar以 UTF-16 格式存储数据,避免代码页转换。 - 如果无法更改列类型,请确认数据库的对齐是否支持你存储的字符集。
启用诊断日志记录
使用 log 连接参数来启用驱动程序级日志:
sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=63
日志标志是位掩码值: 1 (错误)、 2 (消息)、 4 (行)、 8 (SQL)、 16 (参数)、 32 (事务)、 64 (调试)。 通过加值来组合(例如, 63 = 除调试外的所有值, 127 = 全部)。
对于程序日志,可以使用 SetLogger 或 SetContextLogger。 请参阅 日志记录和诊断。
故障排除清单
| 症状 | 第一步 |
|---|---|
| 连接被拒绝 | 确认SQL Server是否运行且TCP/IP已启用。 |
| 登录失败 | 检查凭证和认证模式。 |
| 证书错误 | 检查服务器证书或设置TrustServerCertificate=true(仅限开发环境)。 |
| 连接超时 | 使用 Test-NetConnection 验证网络路径。 检查防火墙规则。 |
| Azure SQL 防火墙 | 将你的 IP 添加到 Azure SQL 防火墙规则中。 |
| 调节错误 | 实现带指数退避的重试。 提升层级。 |
| 连接不良 | 对于 Azure SQL,将ConnMaxIdleTime设置为低于 30 分钟。 实现重试逻辑。 |
| 泳池耗尽 | 显示器 db.Stats()。 修复未关闭的行/交易。 增大 MaxOpenConns。 |
| 慢查询 | 设置上下文超时时间。 查询车辆管理局(DMV)获取昂贵的查询。 |
| 死锁 | 出现 1205 错误时进行重试。 按一致顺序访问表格。 |
| 隐式转换 | 将mssql.VarChar用于varchar列。 |