【发布时间】:2017-08-24 14:50:49
【问题描述】:
一直在用 SqlDataReader 解决一个严重的错误。这段旧代码是在运行 Windows Server 2012 的 AWS 服务器上使用 .NET Framework 3.5 在 Visual Basic 中编写的。数据库是在 Amazon 的 RDS 环境中运行的 Msft Sql Server。
我们开始在日志中看到此错误:
“超时已过期。在操作完成之前超时时间已过或服务器没有响应。”
错误堆栈中的最后一个条目是:
在 System.Data.SqlClient.SqlCommand.ExecuteReader()
这是在我们的 db 包装器中发生错误的代码。
Try
Using connection As New SqlConnection(dbConnStr)
connection.Open()
Using command As New SqlCommand(sql, connection)
Using dataReader As SqlDataReader = command.ExecuteReader()
Do While dataReader.Read()
'do stuff with the results here
Loop
End Using
End Using
End Using
Catch
'write error to log here
End Try
- 这个系统背后的数据库肯定正在工作。我跑了一个跟踪,看到大量的查询被调用。还运行了一个跟踪 过滤以仅显示大约 5 秒的查询,并且没有。
- 触发错误的查询是随机的,绝不相同。当我看到错误时,我可以从日志中获取查询并运行它 手动,它运行迅速/成功。
- 跟踪还表明查询从未在数据库中运行。
- 由于我们在 AWS 中运行,我们启动了另一台服务器并将我们的 AMI 恢复到它,但遇到了同样的问题。
- 错误开始累积后,我们的连接池爆炸了。我们的连接池设置为 700。我们确实添加了这个连接 Lifetime=30 到我们的连接字符串,这使池保持关闭 大约 100 个。
- 我会看到数以千计的查询在错误之间成功运行。
- Windows 和 .NET 均已更新。
我坚持了一些额外的登录,我学到的一件事是 connection.state = 在调用 ExecuteReader 之前打开,并在错误发生之后关闭。
为了缓解这个问题,我构建了一些重试逻辑。如果 ExecuteReader 没有运行,我会退出,等待一秒钟,然后再次调用它。这每次都有效,但会引入 30 秒的延迟。我可能会将 commandTimeout 降低到 30 秒以内,但我真的很感兴趣为什么会首先发生错误。
我已经决定的一件事是连接池处理是艺术而不是科学。在这种情况下,我们的结论是与数据库的连接以某种方式损坏。我们推测新的连接生命周期设置有助于回收池并防止它爆炸,这很好,因为它可以防止该网站出现故障。
有什么建议吗?
【问题讨论】:
标签: .net sql-server database-connection connection-pooling sqldatareader