天天看點

SQL SERVER 臨時表導緻存儲過程重編譯(recompile)的一些探讨

   sqlserver為了確定傳回正确的值,或者處于性能上的顧慮,有意不重用緩存在記憶體裡的執行計劃,而重新編譯執行計劃的這種行為,被稱為重編譯

(recompile)。那麼引發存儲過程重編譯的條件有哪一些呢?下面羅列了一些導緻重編譯(recompile)的條件:

    - 對查詢所引用的表或視圖進行更改(alter table 和 alter view)。

    - 對執行計劃所使用的任何索引進行更改。

    - 對執行計劃所使用的統計資訊進行更新,這些更新可能是從語句(如 update statistics)中顯式生成,也可能是自動生成的。

    - 删除執行計劃所使用的索引。

    - 顯式調用 sp_recompile。

    - 對鍵的大量更改(其他使用者對由查詢引用的表使用 insert 或 delete 語句所産生的修改)。

    - 對于帶觸發器的表,插入的或删除的表内的行數顯著增長。

    - 使用 with recompile 選項執行存儲過程。

    - 有些dbcc freeproccache;分離、附加資料庫、資料更新也會清除記憶體裡緩存的執行計劃

了,切入到今天我們要關注的問題:臨時表的資料變化導緻存儲過程重編譯問題,其實臨時表的資料變化導緻存儲過程重編譯實質上是因為臨時表的資料變化,導緻

了臨時表統計資訊的自動更新,進而引起的重編譯。那麼觸發臨時表的統計資訊的更新的條件或閥值是什麼呢?說來也簡單,就是下面一個這個公式(n表示變更前臨時表的資料記錄數,确切的說是上一次采集統計資訊時臨時表的記錄數)

temporary table

if n < 6, rt = 6.

if 6 <= n <= 500, rt = 500.

if n > 500, rt = 500 + 0.20 * n.

有個網友說存儲過程中的臨時表資料變更的閥值有問題:他的原話如下

if n < 6, recompilation threshold = 6.

if 6 <= n <= 500, recompilation threshold = 500.

上面這兩個區間沒有問題。但是大于500的之後,根本就不是變化大于20%之後再重編譯。看了他提出的問題,其實我也不是特肯定,畢竟沒有實際驗證過。實踐才是檢驗整理的唯一标準,那麼我們就開始做實驗吧,首先準備一下測試環境(microsoft sql server 2008 (rtm) - 10.0.1600.22 (x64) ).腳本如下所示:

準備好測試環境後,那麼此時我們打開sql server工具sql

server profiler,選擇“sp:recompile”和“sp:complete”事件,然後取消一些選擇列,僅僅選擇一些需要的列,例如

eventclass、textdata等。如下所示

SQL SERVER 臨時表導緻存儲過程重編譯(recompile)的一些探讨

開啟profile跟蹤後,我們打開一個會話視窗,勾選“包括實際的執行計劃”,然後再視窗執行下面sql語句

如下所示,實際的執行計劃中,我們看到“估計行數”和“實際行數”是一緻的。

SQL SERVER 臨時表導緻存儲過程重編譯(recompile)的一些探讨

執行上面兩個語句,我們會發現“估計行數”與“實際行數”開始出現偏差,因為資料庫對臨時表#t沒有最新的統計資訊,還是上一次收集的統計資訊時的資料(1行資料)

SQL SERVER 臨時表導緻存儲過程重編譯(recompile)的一些探讨

exec dbo.usp_recompile_test 7;  此時已經觸發了對臨時表統計資訊的采集更新(請見後面闡述)。

SQL SERVER 臨時表導緻存儲過程重編譯(recompile)的一些探讨

那麼執行上面sql語句,130我們确信不會導緻臨時表#t去更新統計資訊,501會觸發#t表的統計資訊更新嗎? 如果不會觸發,那麼确切的值是多少呢?答案是507,如下截圖所示:

SQL SERVER 臨時表導緻存儲過程重編譯(recompile)的一些探讨

想必有些人會說,我實驗的結果不一樣哦(啪啦啪啦說一大堆),那麼你是否真正的了解了下面公式呢? n表示臨時表變跟前的記錄數(确切的說是統計資訊采集時的記錄數),後面的rt表示變跟的記錄數。

由于我第一次執行的是exec dbo.usp_recompile_test

1,那麼資料庫的記錄數為1,那麼1+ 6 =7; 也就是上圖exec dbo.usp_recompile_test

7時才觸發臨時表#t的統計資訊更新,而為什麼是507(7+500=507)呢,因為最後一次統計資訊的采集,臨時表#t的記錄數為7

,是以7+500=507,是否有點不解,那麼你按我這個sql執行一遍,然後用profile跟蹤、你會看到下面結果,如果還不太明白,結合截圖好好理

解一下:

SQL SERVER 臨時表導緻存儲過程重編譯(recompile)的一些探讨

如果還沒有了解的話,我的表達能力已到極限了,自己再好好琢磨一下吧! 那麼接下來才是我們重點想要驗證、測試的。

此時臨時表#t的記錄數為501,那麼當臨時表#t裡面的記錄數變更了多少時,

才會觸發統計資訊的更新呢? 由于是插入,那麼根據公式應該是501 + (500 + 0.2*501) = 1101.2

,那麼應該是1101,即使是1100也不會變化。下面sql server profile可以驗證我們的推測

如果我們繼續使用該存儲過程,那麼當參數為什麼值時才會觸發統計資訊更新呢? 1101 +(500+0.2*1101)=1821.2,也就是說必須是1821才會觸發統計資訊更新,下面sql server profile的截圖也驗證了我們的推測。

SQL SERVER 臨時表導緻存儲過程重編譯(recompile)的一些探讨

是以綜上述實驗驗證,sql server 臨時表導緻存儲過程重編譯(recompile)的那些閥值确實是正确的,也是沒有問題的。當然如有疏漏或不對的地方,敬請指出。