从Web上调用存储过程很慢,快速从Management Studio调用

时间:2011-07-05 15:46:51

标签: asp.net sql-server-2008 stored-procedures

我的存储过程每次从Web应用程序调用时都会疯狂地超时。

我启动了Sql Profiler并跟踪了超时的调用,最后找到了这些东西:

  1. 执行MS SQL Management Studio中的语句时,使用相同的参数(事实上,我从sql profile trace复制了过程调用并运行它):它以5~6秒的平均值完成。
  2. 但是当从Web应用程序调用时,它需要超过30秒(在跟踪中),因此我的网页实际上超时了。
  3. 除了我的Web应用程序有自己的用户之外,每件事都是相同的(相同的数据库,连接,服务器等) 我还尝试使用Web应用程序的用户直接在工作室中运行查询,并且不会超过6秒。

    我如何知道发生了什么?

    我假设它与我们使用BLL>的事实无关。 DAL图层或表适配器作为跟踪清楚地显示延迟在实际过程中。这就是我能想到的全部。

    编辑我在this link中发现ADO.NET将ARITHABORT设置为true - 这在大多数时间都有用,但有时会发生这种情况,以及建议的工作-around是将with recompile选项添加到存储过程。在我的情况下,它不起作用,但我怀疑它与此非常相似。任何人都知道ADO.NET做了什么或者我在哪里可以找到规范?

8 个答案:

答案 0 :(得分:74)

我过去曾遇到过类似的问题,所以我很想看到解决这个问题的方法。 Aaron Bertrand对OP的评论导致了Query times out when executed from web, but super-fast when executed from SSMS,虽然这个问题不重复,但答案很可能适用于您的情况。

实质上,听起来SQL Server可能有一个损坏的缓存执行计划。您正在使用您的Web服务器进行糟糕的计划,但SSMS会出现不同的计划,因为ARITHABORT标志上有不同的设置(否则会对您的特定查询/存储过程没有影响)。

有关其他示例,请参阅ADO.NET calling T-SQL Stored Procedure causes a SqlTimeoutException,并提供更完整的解释和解决方案。

答案 1 :(得分:42)

我还体验到,查询在网络上运行缓慢且在SSMS中运行速度很慢,我最终发现这个问题叫做参数嗅探。

我的修复方法是将sproc中使用的所有参数更改为局部变量。

例如。改变:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
SELECT * FROM Table WHERE ID = @param1 

为:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
DECLARE @param1a int
SET @param1a = @param1
SELECT * FROM Table WHERE ID = @param1a

似乎很奇怪,但它解决了我的问题。

答案 2 :(得分:6)

不要垃圾邮件,但作为其他人希望有用的解决方案,我们的系统会有很高的超时时间。

我尝试使用sp_recompile设置存储过程以重新编译,这解决了一个SP的问题。

最终有更多的SP超时,其中许多以前从未这样做过,使用DBCC DROPCLEANBUFFERSDBBC FREEPROCCACHE事件的超时率大幅下降 - 有仍然是孤立的事件,有些我怀疑计划重建需要一段时间,有些地方的SP真正表现不佳,需要重新评估。

答案 3 :(得分:4)

可能是在Web应用程序调用SP之前进行的其他一些数据库调用是否保持事务处于打开状态?这可能是此SP在Web应用程序调用时等待的原因。我说在Web应用程序中隔离调用(将其放在新页面上)以确保Web应用程序中的某些先前操作导致此问题。

答案 4 :(得分:1)

只需重新编译存储过程(我的情况下的表函数)对我有用

答案 5 :(得分:1)

像@Zane一样,这可能是由于参数嗅探造成的。我经历了相同的行为,我查看了过程的执行计划和sp的所有语句(从过程中复制了所有语句,将参数声明为变量并为变量赋予相同的值)参数有)。然而,执行计划看起来完全不同。 sp执行需要3-4秒,并且会立即返回具有完全相同值的连续语句。

executionplan

经过一些谷歌搜索后,我发现了一个关于这种行为的有趣读物:Slow in the Application, Fast in SSMS?

  

编译过程时,SQL Server不知道@fromdate的值发生了变化,但在假设@fromdate的值为NULL的情况下编译过程。由于所有与NULL的比较都会产生UNKNOWN,如果@fromdate在运行时仍然具有此值,则查询根本不能返回任何行。如果SQL Server将输入值作为最终事实,它可以构建一个仅具有不能访问该表的常量扫描的计划(运行查询SELECT * FROM Orders WHERE OrderDate> NULL以查看此示例)。但是,无论@fromdate在运行时具有什么值,SQL Server都必须生成一个返回正确结果的计划。另一方面,没有义务制定一个对所有价值观都最好的计划。因此,由于假设不会返回任何行,因此SQL Server将为索引搜索结算。

问题是我的参数可以保留为null,如果它们作为null传递,则会使用默认值进行初始化。

create procedure dbo.procedure
    @dateTo datetime = null
begin
    if (@dateTo is null)
    begin
        select @dateTo  = GETUTCDATE()
    end

    select foo
    from dbo.table
    where createdDate < @dateTo
end

我把它改成

create procedure dbo.procedure
    @dateTo datetime = null
begin
    declare @to datetime = coalesce(@dateTo, getutcdate())

    select foo
    from dbo.table
    where createdDate < @to
end 

它再次像魅力一样。

答案 6 :(得分:0)

--BEFORE
CREATE PROCEDURE [dbo].[SP_DEMO]
( 
    @ToUserId bigint=null
 )
AS
BEGIN
SELECT * FROM tbl_Logins WHERE LoginId = @ToUserId 
END
--AFTER CHANGING TO IT WORKING FINE
CREATE PROCEDURE [dbo].[SP_DEMO]
( 
    @ToUserId bigint=null
 )
AS
BEGIN
DECLARE @Toid bigint=null
SET @Toid=@ToUserId
SELECT * FROM tbl_Logins WHERE LoginId = @Toid 
END

答案 7 :(得分:0)

您可以通过以下方式定位特定的缓存执行计划:

SELECT cp.plan_handle, st.[text]
FROM sys.dm_exec_cached_plans AS cp 
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS st
WHERE [text] LIKE N'%your troublesome SP or function name etc%'

然后只删除导致问题的执行计划,例如:

DBCC FREEPROCCACHE (0x050006003FCA862F40A19A93010000000000000000000000)

我现在有一个每 5 分钟运行一次的作业,它会查找运行缓慢的过程或函数,并在发现任何执行计划时自动清除这些执行计划:

if exists (
    SELECT cpu_time, *
    FROM sys.dm_exec_requests req
    CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext
    --order by req.total_elapsed_time desc
    WHERE ([text] LIKE N'%your troublesome SP or function name etc%')
    and cpu_time > 8000
)
begin

    SELECT cp.plan_handle, st.[text]
    into #results
    FROM sys.dm_exec_cached_plans AS cp 
    CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS st
    WHERE [text] LIKE N'%your troublesome SP or function name etc%'
    delete #results where text like 'SELECT cp.plan_handle%'
    --select * from #results

    declare @handle varbinary(max)
    declare @handleconverted varchar(max)
    declare @sql varchar(1000)

    DECLARE db_cursor CURSOR FOR  
    select plan_handle from #results

    OPEN db_cursor   
    FETCH NEXT FROM db_cursor INTO @handle

    WHILE @@FETCH_STATUS = 0   
    BEGIN   

        --e.g. DBCC FREEPROCCACHE (0x050006003FCA862F40A19A93010000000000000000000000)
        print @handle
        set @handleconverted = '0x' + CAST('' AS XML).value('xs:hexBinary(sql:variable("@handle"))', 'VARCHAR(MAX)')
        print @handleconverted
        set @sql = 'DBCC FREEPROCCACHE (' + @handleconverted + ')'
        print 'DELETING: ' + @sql
        EXEC(@sql)

        FETCH NEXT FROM db_cursor INTO @handle
    END   

    CLOSE db_cursor   
    DEALLOCATE db_cursor

    drop table #results

end