2013-05-29 73 views
15

我在SQL Server 2008 R2上有一個很奇怪的存儲過程問題。有時候,大約每個月一次,我有一個過程變得非常緩慢,大約需要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 
+0

看看這是否有幫助.. http://msdn.microsoft.com/en-us/library/ms190439(v=sql.105).aspx – Elmer

回答

23

當你第一次編譯存儲過程,它的執行計劃被緩存。

如果存儲過程具有參數,其定義可以顯著改變包含查詢的執行計劃(如索引掃描VS尋求),存儲過程的緩存計劃可能不適用於所有參數的定義是最好的。爲了避免這種

一種方式是包括與CREATE PROCEDURE語句RECOMPILE條款。

實施例:

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

通過這樣做,一個新的計劃將被每個程序被調用時生成的。如果recompile time < time lost by its using the wrong cached plan,這值得使用WITH RECOMPILE。在你的情況下,它會爲你節省每次你注意到執行緩慢時手動重新編譯這個過程所需的時間/計劃。

+1

如果我們是RECOMPILE程序,每當我們通過應用程序執行時,它會影響性能嗎? –

+0

謝謝。爲我工作。我卡住了。 SQL 2012工作正常。但SQL 2008服務器無法正常工作。這節省了我... –

2

對於許多參數的存儲過程,這將是明智的,在查詢的末尾添加OPTION(OPTIMIZE FOR UNKNOWN)告訴編譯器不優化特定參數的執行計劃。

SQL Server在首次運行存儲過程時執行的操作是優化傳遞給它的參數的執行計劃。這是在一個稱爲Parameter Sniffing的過程中完成的。

一般情況下,執行計劃由SQL Server緩存,以便SQL Server不必每次都重新編譯相同的查詢。下一次運行該過程時,SQL Server將重新使用執行計劃(s)進行查詢......但是,如果您使用不同的參數調用存儲過程,那麼執行計劃可能完全沒有效率。

添加我提到會告訴給SQL編譯器的執行計劃不應該爲具體參數進行優化選擇,而對於傳遞給存儲過程的任何參數。從documentation

OPTIMIZE未知

指示查詢優化器在查詢編譯和優化,包括強制參數化創建的參數,而不是使用爲所有本地變量的初始值的統計數據。

通過@sion_corn答案建議添加WITH RECOMPILE存儲過程的定義,然而,這迫使每次執行存儲過程時整個語句的重新編譯。如果程序經常被調用,這可能會產生不可接受的開銷。

+1

你好,我有一個存儲過程,從SQL Server執行少於3s,並從未完成(具有非常高的CPU使用率)從Entity框架執行相同的參數。執行DBCC DROPCLEANBUFFERS和DBCC FREEPROCCACHE清除問題。添加WITH RECOMPILE似乎也起作用。但是如果我按照你的建議,SP完成比WITH RECOMPILE要花費幾秒鐘的時間。所以對我來說,即使我提高了答案(我不知道優化未知),WITH RECOMPILE似乎是最好的選擇,因爲它提供了最好的性能。 – AFract

+0

@AFract事實上,在重新編譯開銷超過特定參數執行計劃優化的情況下,「WITH RECOMPILE」可能比「OPTIMIZE FOR UNKNOWN」更好。 –

相關問題