存储过程很慢,代码比SSMS长3倍

时间:2014-11-08 06:05:31

标签: asp.net sql-server vb.net timeoutexception

我遇到了与“无法找到表格0”相关的问题。最初我不知道找到这个例外的根本问题。然后我才知道这个问题是由于“Timeout Expired”错误引起的。操作完成之前经过的超时时间或服务器没有响应“。 (即)在SQL Server 2005中执行存储过程需要超过3秒的时间(默认查询超时为3秒)。所以我在SSMS(SQL Sever Management Studio)中使用相同的参数执行了相同的存储过程。但它只花了1秒钟。

在中间,我们在另一台服务器(备份服务器)中运行相同的源代码和SQL代码。但没有错误。 (即)整个过程(我有一个while循环,循环40次,包括4循环,循环大约60次。每次它将访问3个存储过程)在我的系统中花了30分钟,但在备份服务器只有3分钟已被采取,这意味着没有超时。但所有来源都是一样的。所以现在我结束了涉及的技术问题。

I tried the following things in my source.
•   In Asp.net, I added the “SQLCommand Timeout = 0”. But it is a failure.
•   I used “SET ArithAbort ON” and “With Recompile”. It is also a failure.
•   Then I used “Parameter Sniffing”. (i.e) I used local variables in stored procedures. But all went in a wrong direction.

The Ado.net code for accessing the stored procedure in asp.net is like the following:

 Public Function retds1(ByVal SPName As String, ByVal conn As SqlConnection, Optional ByVal ParameterValues() As Object = Nothing) As DataSet
    dconn = New SqlConnection(ConfigurationManager.ConnectionStrings("webriskpro").ConnectionString)
    Try
        sqlcmd = New SqlCommand
        ds = New DataSet
        If dconn.State = ConnectionState.Open Then dconn.Close()
        sqlcmd = New SqlCommand(SPName, dconn)
        sqlcmd.CommandType = CommandType.StoredProcedure
        sqlcmd.CommandTimeout = 0
        dconn.Open()
        SqlCommandBuilder.DeriveParameters(sqlcmd)
        If Not ParameterValues Is Nothing Then
            For i As Integer = 1 To ParameterValues.Length
                sqlcmd.Parameters(i).Value = ParameterValues(i - 1)
            Next
        End If
        da = New SqlDataAdapter(sqlcmd)
        da.SelectCommand.CommandTimeout = 0
        da.Fill(ds)
    Catch ex As Exception
        send_prj_err2mail(ex, SPName, "")
    Finally
        dconn.Close()
    End Try
    Return ds
End Function

错误提供程序的执行计划是

enter image description here

希望你们明白我的问题。请给我一些想法!!

4 个答案:

答案 0 :(得分:2)

您需要调试应用程序以找出根本原因。在另一个帖子中你提到:

  

我已在SQL Server Profiler中检查过程。由于循环没有限制,我们不知道它迭代了多少次。

您需要将文本文件写入代码循环的次数,与PROD或本地PC上的大数字相比,它可能是Backup中的一个小数字。您还应该注明操作的每个部分需要多长时间,使用StopWatch

示例代码
Dim sw as New StopWatch()
sw.Start()

...long operation...

sw.Stop()
File.Write("Operation XYZ took " & sw.EllapsedMilliseconds / 1000 & " seconds")

我对这里的代码持怀疑态度,看起来并不是很好。例如,在实例化之后检查连接是否打开,这会导致资源管理不畅。它就像这段代码的作者一样,发现连接处于打开状态并且未在其他地方关闭:

dconn = New SqlConnection(ConfigurationManager.ConnectionStrings("webriskpro").ConnectionString)
...
If dconn.State = ConnectionState.Open Then dconn.Close() 

我建议您使用Using语句,保证关闭连接,例如:

Using cn As New SqlConnection(ConnectionString)
cn.Open()

  Using cmd As New SqlCommand("GetCustomerByID", cn)
    Try
        With cmd
            .Connection = cn
            .CommandType = CommandType.StoredProcedure
            .Parameters.Add("@CustomerID", SqlDbType.Int, 4)
            .Parameters("@CustomerID").Value = CustomerID
        End With

        da = New SqlDataAdapter(cmd)
        da.Fill(ds, "Customer")
    Catch ex As Exception

    End Try
  End Using
End Using

虽然你知道If the connection is closed before Fill is called, it is opened to retrieve data, then closed. If the connection is open before Fill is called, it remains open.所以你甚至不需要在你的情况下明确地打开它。

再次您将需要调试应用程序以找出根本原因。添加大量日志记录以帮助诊断问题。调试问题应该有助于解决这个性能瓶颈的根本原因。

祝你好运!!

编辑:对于此处提出的任何连接池构思,您需要使用ConnectionPooling,因此请从连接字符串中删除Pooling = false:

<add name="webriskpro" 
     connectionString="Data Source=TECH01\SQL2005;Initial Catalog=webriskpro1;User ID=sa;Password=#basix123; <strike>pooling=false</strike>;connection

超时= 600;“/&GT;

编辑2:一些帮助您解决问题的研究

  1. 运行SQLDiag作为产品的一部分。您可以在线推荐图书以获取更多详细信息简而言之,捕获服务器端跟踪和阻止程序脚本。

  2. 捕获跟踪后,查找“注意”事件。这将是收到错误的spid。如果按SPID过滤,则会在“注意”之前看到RPC:已完成事件。检查那边的时间。那是30秒吗?如果是,那么客户端等待30秒以从SQL获得响应并且“超时”[这是客户端设置,因为SQL永远不会停止和连接]

  3. 现在,检查运行的查询是否需要30秒?

  4. 如果是,则调整查询或从客户端增加超时设置。

  5. 如果否,则此查询必须等待某些资源(已阻止)

  6. 此时返回阻止程序脚本并查看“注意”来的时间范围

  7. 如果你可以在查询超时时执行sp_who2,你可以使用BlkBy列追溯到持有其他人正在等待的锁。

    sp_who3也很有用,因为它包含实际查询。

    当您看到哪些查询/事务锁定/阻止您的数据库直到完成时,您可能需要重写它们或在其他时间运行它们以避免阻止其他进程。

    要深入研究的是事务日志和数据库的自动增量大小。将它们设置为固定大小而不是当前文件的百分比。如果文件大于分配足够空间所花费的时间并且比事务超时时间长,则数据库将停止运行。

答案 1 :(得分:1)

一些想法:

  • 检查主服务器是否存在资源不足问题:内存,磁盘空间,运行和使用资源的许多其他进程等等。
  • 检查主服务器是否存在配置问题:数据库配置为仅使用少量内存,系统交换文件太小等等。
  • 检查主服务器是否存在硬件问题:内存错误,RAID故障,驱动器故障等等。

希望有所帮助!

答案 2 :(得分:1)

我认为异常是因为连接开放时间。

您需要为连接指定超时,或从连接字符串中删除pooling = false。

这样给,

<add name="db1" connectionString="Data Source=your server;Initial Catalog=dbname;User ID=sa;Password=*****;connection timeout=600;"/>

池的默认大小为100.如果您想要更改,则可以。

由于在备份服务器中没有应用程序在运行,因此系统的速度会很高,并且不会出现中断。

但是在你的本地系统中,你可能已经开发了其他一些应用程序。所以这可能就是其中一个原因。下次运行时,关闭所有打开的应用程序,然后检入sql profiler。

由于相同的代码在备份服务器中工作正常,我不认为循环(60 * 40次)不会减慢速度。

祝你好运!!

答案 3 :(得分:0)

如果错误仅在高负载时发生,则很可能没有将连接返回到池中,并且与事务保持关联的时间超过了它们应该的时间。如果事务速度不是问题,请尝试以下查询:

SqlConnection.ClearPool(connection);

注意:对于SQL中的每个操作,我们必须打开一个连接,因此在打开连接后提供此查询

conn.Open()

cmd.ExecuteNonQuery()

SQLConnection.ClearPool(康涅狄格州)