【问题标题】:Possible connection leaking causing "System.Data.SqlClient.SqlException: Timeout expired" error in SQL Server?可能的连接泄漏导致 SQL Server 中出现“System.Data.SqlClient.SqlException: Timeout expired”错误?
【发布时间】:2011-02-17 19:39:36
【问题描述】:

我的应用程序要求用户登录并允许他们编辑内容列表。但是,似乎如果同一个用户总是登录和注销并编辑列表,这个用户会遇到“System.Data.SqlClient.SqlException: Timeout expired”。错误。我已经阅读了可能由未提交的事务引起的评论。我确实有一个在申请中。

我将提供我正在使用的代码,其中有一个 IF 语句,我对此有点怀疑,但这似乎是一个合理的做法。

我将简单介绍一下这里发生的情况,有一个要更新或添加到数据库中的对象列表。在应用程序中创建的新对象的 ID 为 0,而现有对象具有从数据库生成的自己的 ID。如果用户选择删除某些对象,它们的 ID 将存储在单独的整数列表中。一旦用户准备好保存他们的更改,这两个列表就会传递给这个方法。通过使用 IF 语句,添加 ID 为 0 的对象(使用 Add 存储过程)并更新那些 ID 非零的对象(使用 Update 存储过程)。毕竟,FOR 循环遍历“删除”列表中的所有整数,并使用 Delete 存储过程来删除它们。所有这些都需要一个事务。

Public Shared Sub UpdateSomethings(ByVal SomethingList As List(Of Something), ByVal RemovalList As List(Of Integer))
Using DBConnection As New SqlConnection(conn)
    DBConnection.Open()
    Dim MyTransaction As SqlTransaction
    MyTransaction = DBConnection.BeginTransaction()
    Try
        Using MyCommand As New SqlCommand()
            MyCommand.Transaction = MyTransaction
            MyCommand.CommandType = CommandType.StoredProcedure

            For Each SomethingItem As Something In SomethingList
                MyCommand.Connection = DBConnection
                If SomethingItem.ID > 0 Then
                    MyCommand.CommandText = "UpdateSomething"
                Else
                    MyCommand.CommandText = "AddSomething"
                End If
                MyCommand.Parameters.Clear()
                With MyCommand.Parameters
                    If MyCommand.CommandText = "UpdateSomething" Then
                        .Add("@id", SqlDbType.Int).Value = SomethingItem.ID
                    End If
                    .Add("@stuff", SqlDbType.Varchar).Value = SomethingItem.Stuff
                End With
                MyCommand.ExecuteNonQuery()
            Next

            MyCommand.CommandText = "DeleteSomething"
            For Each ID As Integer In RemovalList
                MyCommand.Parameters.Clear()
                With MyCommand.Parameters
                    .Add("@id", SqlDbType.Int).Value = ID
                End With
                MyCommand.ExecuteNonQuery()

            Next
        End Using
        MyTransaction.Commit()
    Catch ex As Exception
        MyTransaction.Rollback()
        'Exception handling goes here   '
    End Try

End Using
End Sub

这里使用了三个存储过程以及一些循环,因此如果列表足够大,我可以看到某些东西如何保持所有内容。

我正在使用 Visual Studio 2008 进行调试,并且正在将 SQL Server 2000 用于 DB。

编辑:我似乎仍然收到此错误。我什至删除了整个交易的东西,我仍然遇到它。在这一点上,我假设这里发生了某种泄漏。我试过不使用 USING 语句并明确告诉命令和连接自行处理,但没有骰子。如果在短时间内大量调用此方法,SQL Server 的内存使用量也会增加不少。

我读到增加 SQLCommand 的 CommandTimeout 属性会有所帮助。我想知道这样做是否有什么大的缺点或后果。

【问题讨论】:

  • 顺便说一句,我认为您应该在 catch 子句的回滚后添加一个 Rethrow 。您实际上还没有处理异常,因此您应该允许它传播。
  • 我一定是不小心删除了我发布的示例中的异常处理,但感谢您指出。 :)

标签: vb.net transactions sql-server-2000 timeout


【解决方案1】:

我建议使用以下方法,这样Dispose 将始终被调用并在每个未提交的情况下被回滚。

using (SqlConnection sqlCn = new SqlConnection())
{
   using (SqlTransaction myTrans = sqlCn.BeginTransaction())
   {
   ...
   myTrans.Commit();
   }
}

另外,我认为您不需要为每次执行创建一个新的 SqlCommand。只需保持相同并更新 CommandText 和 Parameters。

【讨论】:

  • 感谢您的建议。我会看看这是否能解决问题,因为这确实有意义。
【解决方案2】:

如果您有大量命令,您可能希望在打开连接之前全部构建它们。启动事务并打开连接后,旋转并执行它们。

你可能想使用 TransactionScope

Using _tx as New System.Transactions.TransactionScope(<add your own timeout here>)

 'Do all your sql work' 

 If _noErrors Then
  _tx.Complete()
 End If

End Using 

使用事务范围,您可以在不修改服务器设置的情况下设置最长 20 分钟的超时时间。

【讨论】:

  • 超时时间增加这么多有什么坏处吗?会影响性能吗?
  • 是的。对于任何事务,它打开的时间越长,它正在使用的资源被其他进程阻止的时间就越长。通常对于数据库,您希望在打开的连接上花费尽可能少的时间(因此首先创建所有命令对象)。还有其他批处理 SQL 的方法(Ms Enterprise 库数据 - msdn.microsoft.com/en-us/library/ff380164(v=PandP.31).aspx)可能更适合您。
【解决方案3】:

我相信我已经设法解决了这个问题。我已经修改了应用程序,以便不会对数据库进行不必要的调用(即不需要再次更新未更改的对象)并增加了 SQLCommand 对象的 CommandTimeout 属性。到目前为止,没有任何问题。

也非常感谢您的建议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-25
    • 2010-09-13
    • 2013-05-30
    相关资源
    最近更新 更多