Stored procedures 存储过程在重新编译后快速运行

Stored procedures 存储过程在重新编译后快速运行,stored-procedures,sql-server-2008-r2,Stored Procedures,Sql Server 2008 R2,我对SQLServer2008R2上的存储过程有一个非常奇怪的问题。有时,大约每个月一次,我有一个过程变得非常慢,需要大约6秒来运行,而不是几毫秒。但如果我只是重新编译它,而不做任何更改,它会再次快速运行。 并不是所有存储过程都会发生这种情况,只有一个存储过程在服务器上有几百个存储过程 我的猜测是,在编译sp时,它会被缓存,并且每次调用它时都会重用该缓存,而该缓存版本会由于某种原因而损坏 我希望可能有些人已经遇到了这种问题,或者至少可以为我指出正确的方向,比如SQL Server或IIS的什么配

我对SQLServer2008R2上的存储过程有一个非常奇怪的问题。有时,大约每个月一次,我有一个过程变得非常慢,需要大约6秒来运行,而不是几毫秒。但如果我只是重新编译它,而不做任何更改,它会再次快速运行。 并不是所有存储过程都会发生这种情况,只有一个存储过程在服务器上有几百个存储过程

我的猜测是,在编译sp时,它会被缓存,并且每次调用它时都会重用该缓存,而该缓存版本会由于某种原因而损坏

我希望可能有些人已经遇到了这种问题,或者至少可以为我指出正确的方向,比如SQL Server或IIS的什么配置可能会影响存储过程缓存

代码如下:

USE [MyBaseName]
GO
/****** Object:  StoredProcedure [dbo].[Publication_getByCriteria]    Script Date: 05/29/2013 12:11:07 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[Publication_getByCriteria]
    @id_sousTheme As int = null,
    @id_theme As int = null,
    @nbPubli As int = 1000000,
    @bActuSite As bit = null,
    @bActuPerso As bit = null,
    @bActuNewsletter As bit = null,
    @bActuChronique As bit = null,
    @bActuVideo As bit = null,
    @bActuVideoBuzz As bit = null,
    @bActuOpportunite As bit = null,
    @id_contact As int = null,
    @bOnlyPublished As bit = 0,
    @bOnlyForHomePage as bit = 0,
    @id_contactForTheme As int = null,
    @id_newsletter As int = null,
    @ID_ActuChronique As int = null,
    @sMotClef As varchar(500) = null,
    @sMotClefForFullText as varchar(500) = '""',
    @dtPublication As datetime = null,  
    @bParlonsFinance As bit = null,
    @bPartenaires as bit = null,
    @bUne As bit = null,
    @bEditoParlonsFinance As bit = null,
    @bEditoQuestionFonds as bit = null,
    @dtDebPublication As datetime = null,
    @dtFinPublication As datetime = null,
    @bOnlyActuWithDroitReponse As bit = 0,
    @bActuDroitReponse As bit = null
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @dtNow As datetime
    SET @dtNow = GETDATE()

    SELECT TOP (@nbPubli) p.id_publication, p.sTitre, p.sTexte, p.sTexteHTML, p.dtPublication, p.id_linkedDroitReponse,
        si.id_actusite, pe.id_actuPerso, ne.id_actuNewsletter, ac.id_actuChronique, av.id_actuVideo, ap.id_actuOpportunite, ad.id_actuDroitReponse,
        c.ID_Contact, c.sPhotoCarre, NULL As sTypePubli, n.id_newsletter, 
        dbo.Publication_get1Theme(p.id_publication) As theme,
        CAST(CASE WHEN ad.id_actuDroitReponse IS NULL THEN 0 ELSE 1 END As bit) As bIsDroitReponse,
        coalesce(Personne.sNom, Societe.sNom) as sNom, Personne.sPrenom
    FROM Publication p
        LEFT OUTER JOIN ActuSite si ON p.id_publication = si.id_publication
        LEFT OUTER JOIN ActuPerso pe ON p.id_publication = pe.id_publication
        LEFT OUTER JOIN ActuNewsletter ne ON p.id_publication = ne.id_publication
        LEFT OUTER JOIN ActuChronique ac ON p.id_publication = ac.id_publication
        LEFT OUTER JOIN ActuVideo av ON p.id_publication = av.id_publication
        LEFT OUTER JOIN ActuOpportunite ap ON p.id_publication = ap.id_publication
        LEFT OUTER JOIN ActuDroitReponse ad ON p.id_publication = ad.id_publication
        LEFT OUTER JOIN Contact c ON p.id_contact = c.ID_Contact
        LEFT OUTER JOIN Personne ON Personne.id_contact = c.id_contact
        LEFT OUTER JOIN Societe ON Societe.id_contact = c.id_contact
        LEFT OUTER JOIN Newsletter n ON ne.id_actuNewsletter = n.id_actuNewsletter
    WHERE p.bSupp = 0
    AND (@bOnlyPublished = 0 Or (@bOnlyPublished = 1 AND p.dtPublication IS NOT NULL AND p.dtPublication < @dtNow))
    AND (@id_sousTheme IS NULL Or p.id_publication IN(SELECT id_publication FROM PubliSousTheme WHERE id_soustheme = @id_sousTheme))
    AND (@id_theme IS NULL Or p.id_publication IN(SELECT id_publication FROM PubliTheme WHERE id_theme = @id_theme))
    AND ((@bActuSite = 1 AND si.id_actusite IS NOT NULL)
            OR (@bActuPerso = 1 AND pe.id_actuPerso IS NOT NULL)
            OR (@bActuNewsletter = 1 AND ne.id_actuNewsletter IS NOT NULL)
            OR (@bActuChronique = 1 AND ac.id_actuChronique IS NOT NULL)
            OR (@bActuVideo = 1 AND av.id_actuVideo IS NOT NULL)
            OR (@bActuVideoBuzz = 1 AND av.id_actuVideo IS NOT NULL and coalesce(av.sBuzz, '') <> '' )
            OR (@bActuOpportunite = 1 AND ap.id_actuOpportunite IS NOT NULL)
            OR (@bActuDroitReponse = 1 AND ad.id_actuDroitReponse IS NOT NULL))
    AND (@id_contact IS NULL Or p.id_contact = @id_contact)
    AND (@id_contactForTheme IS NULL Or 
            (p.id_publication IN(SELECT id_publication FROM PubliSousTheme 
                WHERE id_soustheme IN(SELECT id_soustheme FROM ContactSousTheme WHERE id_contact = @id_contactForTheme)))
            Or (p.id_publication IN(SELECT id_publication FROM PubliTheme 
                WHERE id_theme IN(SELECT id_theme FROM ContactTheme WHERE id_contact = @id_contactForTheme)))
            )
    AND (@ID_ActuChronique is NULL or id_actuChronique = @ID_ActuChronique)
    AND (@id_newsletter IS NULL Or p.id_publication IN(SELECT id_publication FROM ListActuNewsletter WHERE id_newsletter = @id_newsletter))
    AND (@sMotClef IS NULL 
        or contains((p.sTexte, p.sTitre), @sMotClefForFullText)
        Or Personne.sNom LIKE '%' + @sMotClef + '%' COLLATE Latin1_General_CI_AI
        Or Personne.sPrenom LIKE '%' + @sMotClef + '%' COLLATE Latin1_General_CI_AI
        Or Societe.sNom LIKE '%' + @sMotClef + '%' COLLATE Latin1_General_CI_AI
        )
    AND (@dtPublication IS NULL Or p.dtPublication >= @dtPublication)
    AND (
        @bParlonsFinance IS NULL Or
        (@bParlonsFinance = 0 AND p.id_publication NOT IN(SELECT id_publication FROM PubliTheme 
                WHERE id_theme IN(SELECT id_theme FROM Theme WHERE bParlonsFinance = 1)))
        Or (@bParlonsFinance = 1 AND p.id_publication IN(SELECT id_publication FROM PubliTheme 
                WHERE id_theme IN(SELECT id_theme FROM Theme WHERE bParlonsFinance = 1))))
    AND (
        @bPartenaires IS NULL Or
        (@bPartenaires = 0 AND p.id_publication NOT IN(SELECT id_publication FROM PubliTheme 
                WHERE id_theme IN(SELECT id_theme FROM Theme WHERE 0 = 1)))
        Or (@bPartenaires = 1 AND p.id_publication IN(SELECT id_publication FROM PubliTheme 
                WHERE id_theme IN(SELECT id_theme FROM Theme WHERE 0 = 1))))
    AND (
        @bUne IS NULL
        Or p.bUne = @bUne)
    AND (@bEditoParlonsFinance IS NULL
        Or p.bEditoParlonsFinance = @bEditoParlonsFinance)
        AND (@bEditoQuestionFonds IS NULL
        Or p.bEditoQuestionFonds = @bEditoQuestionFonds)
    AND (@dtDebPublication IS NULL Or p.dtPublication >= @dtDebPublication)
    AND (@dtFinPublication IS NULL Or p.dtPublication <= @dtFinPublication)
    AND (@bOnlyActuWithDroitReponse = 0 Or (@bOnlyActuWithDroitReponse = 1 AND p.id_linkedDroitReponse IS NOT NULL))
    and (@bOnlyForHomePage = 0 or (@bOnlyForHomePage = 1 and ac.bHomePage = 1))
    ORDER BY coalesce(p.dtPublication, p.dtCreate) DESC, p.id_publication DESC
END

当您第一次编译存储过程时,它的执行计划将被缓存

如果存储过程包含的参数的定义会显著更改所包含查询的执行计划(例如索引扫描与查找),则存储过程的缓存计划可能不会对所有参数定义都起到最佳作用

避免这种情况的一种方法是在CREATEPROCEDURE语句中包含RECOMPILE子句

例如:

CREATE PROCEDURE dbo.mySpro
@myParam
WITH RECOMPILE
AS
BEGIN
 -- INSERT WORKLOAD HERE
END
GO

这样,每次调用过程时都会生成一个新的计划。如果重新编译时间<由于使用错误的缓存计划而损失的时间,则值得与重新编译一起使用。在您的情况下,它还将为您节省每次注意到该过程执行缓慢时手动重新编译该过程所需的时间/计划。

对于具有如此多参数的存储过程,明智的做法是在查询的末尾添加optionoptimizeforunknown,告诉编译器不要为特定参数优化执行计划

SQL Server在第一次运行存储过程时所做的是优化传递给它的参数的执行计划。这是在一个称为参数嗅探的过程中完成的

通常,执行计划由SQL Server缓存,这样SQL Server就不必每次都为同一查询重新编译。下次运行该过程时,SQL Server将重新使用其中查询的执行计划。。。但是,如果使用不同的参数调用存储过程,执行计划可能会完全无效

添加我提到的选项将告诉SQL编译器,执行计划不应针对特定参数进行优化,而应针对传递给存储过程的任何参数进行优化。从:

为未知优化

指示查询优化器在编译和优化查询时使用统计数据而不是所有局部变量的初始值,包括使用强制参数化创建的参数


@sion_corn的答案建议在存储过程定义中添加WITH RECOMPILE,但是这会强制在每次执行存储过程时重新编译整个语句。如果经常调用该过程,则可能会产生不可接受的开销。

请查看这是否有帮助。。如果我们每次通过应用程序执行时都重新编译一个过程,它会影响性能吗?谢谢。为我工作。我被卡住了。SQL 2012运行良好。但是SQLServer2008Server不工作。这救了我…您好,我有一个存储过程,它从SQL Server执行的时间不到3秒,并且在使用相同参数从Entity framework执行的CPU使用率非常高的情况下永远不会完成。执行DBCC DROPCLEANBUFFERS和DBCC FREEPROCCACHE可以清除该问题。使用重新编译进行添加似乎也有效。但是,如果我遵循您的建议,SP完成所需的时间比重新编译所需的时间多几秒钟。因此,对我来说,即使我对你的答案投了赞成票,我也不知道“为未知优化”,但使用“重新编译”似乎是最好的选择,因为它提供了最好的性能。@事实上,如果特定参数的执行计划优化超过了“为未知优化”,则使用“重新编译”可能比使用“为未知优化”更好。