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) ).指令碼如下所示:
USE MyDBA;
GO
IF EXISTS(SELECT 1 FROM sys.sysobjects WHERE id=object_id(N'[dbo].[TEST]') AND OBJECTPROPERTY(id, N'IsTable')=1 )
BEGIN
DROP TABLE dbo.TEST;
CREATE TABLE TEST
(
ID INT IDENTITY(1, 1) ,
NAME VARCHAR(40) ,
CONSTRAINT PK_TEST PRIMARY KEY(ID)
)
END
GO
INSERT INTO TEST VALUES(NEWID())
GO 10000
CREATE PROCEDURE Usp_Recompile_TEST(@Index INT)
AS
BEGIN
CREATE TABLE #T(ID INT , NAME VARCHAR(40));
INSERT INTO #T SELECT ID, NAME FROM TEST WHERE ID <=@Index;
SELECT m.* FROM #T m INNER JOIN TEST n ON m.ID = n.ID
END
GO
準備好測試環境後,那麼此時我們開啟SQL Server工具SQL Server Profiler,選擇“SP:Recompile”和“SP:Complete”事件,然後取消一些選擇列,僅僅選擇一些需要的列,例如 EventClass、TextData等。如下所示
開啟Profile跟蹤後,我們開啟一個會話視窗,勾選“包括實際的執行計劃”,然後再視窗執行下面SQL語句
EXEC dbo.Usp_Recompile_TEST 1;
如下所示,實際的執行計劃中,我們看到“估計行數”和“實際行數”是一致的。
EXEC dbo.Usp_Recompile_TEST 2;
EXEC dbo.Usp_Recompile_TEST 6;
執行上面兩個語句,我們會發現“估計行數”與“實際行數”開始出現偏差,因為資料庫對臨時表#T沒有最新的統計資訊,還是上一次收集的統計資訊時的資料(1行資料)
EXEC dbo.Usp_Recompile_TEST 7; 此時已經觸發了對臨時表統計資訊的採集更新(請見後面闡述)。
EXEC dbo.Usp_Recompile_TEST 130;
EXEC dbo.Usp_Recompile_TEST 500;
EXEC dbo.Usp_Recompile_TEST 506;
EXEC dbo.Usp_Recompile_TEST 507;
那麼執行上面SQL語句,130我們確信不會導致臨時表#T去更新統計資訊,501會觸發#T表的統計資訊更新嗎? 如果不會觸發,那麼確切的值是多少呢?答案是507,如下截圖所示:
想必有些人會說,我實驗的結果不一樣哦(啪啦啪啦說一大堆),那麼你是否真正的理解了下面公式呢? n表示臨時表變跟前的記錄數(確切的說是統計資訊採集時的記錄數),後面的RT表示變跟的記錄數。
Temporary table
- If n < 6, RT = 6.
- If 6 <= n <= 500, RT = 500.
- If n > 500, RT = 500 + 0.20 * n.
由於我第一次執行的是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跟蹤、你會看到下面結果,如果還不太明白,結合截圖好好理解一下:
DBCC FREEPROCCACHE;
EXEC dbo.Usp_Recompile_TEST 2;
EXEC dbo.Usp_Recompile_TEST 6;
EXEC dbo.Usp_Recompile_TEST 7;
EXEC dbo.Usp_Recompile_TEST 8;
如果還沒有理解的話,我的表達能力已到極限了,自己再好好琢磨一下吧! 那麼接下來才是我們重點想要驗證、測試的。
DBCC FREEPROCCACHE;
EXEC dbo.Usp_Recompile_TEST 501;
此時臨時表#T的記錄數為501,那麼當臨時表#T裡面的記錄數變更了多少時,才會觸發統計資訊的更新呢? 由於是插入,那麼根據公式應該是501 + (500 + 0.2*501) = 1101.2 ,那麼應該是1101,即使是1100也不會變化。下面SQL Server Profile可以驗證我們的推測
EXEC dbo.Usp_Recompile_TEST 1100;
EXEC dbo.Usp_Recompile_TEST 1101;
如果我們繼續使用該儲存過程,那麼當引數為什麼值時才會觸發統計資訊更新呢? 1101 +(500+0.2*1101)=1821.2,也就是說必須是1821才會觸發統計資訊更新,下面SQL Server Profile的截圖也驗證了我們的推測。
EXEC dbo.Usp_Recompile_TEST 1300;
EXEC dbo.Usp_Recompile_TEST 1320;
EXEC dbo.Usp_Recompile_TEST 1321;
EXEC dbo.Usp_Recompile_TEST 1820;
EXEC dbo.Usp_Recompile_TEST 1821;
所以綜上述實驗驗證,SQL SERVER 臨時表導致儲存過程重編譯(recompile)的那些閥值確實是正確的,也是沒有問題的。當然如有疏漏或不對的地方,敬請指出。
相關文章
- Sql儲存過程分頁--臨時表儲存SQL儲存過程
- dbms_lob儲存過程導致臨時表空間100%儲存過程
- SQL Server的巢狀儲存過程中使用同名的臨時表怪像淺析SQLServer巢狀儲存過程
- SQL server儲存過程函式SQLServer儲存過程函式
- SQL Server儲存過程的優缺點SQLServer儲存過程
- Q&A:在SQL Server 2005中編寫儲存過程RVSQLServer儲存過程
- SQL SERVER儲存過程AS和GO的含義SQLServer儲存過程Go
- 【SQL Server】常見系統儲存過程SQLServer儲存過程
- SQL Server 2005的複製儲存過程選項BYSQLServer儲存過程
- Ms Sql Server查詢儲存過程中的內容SQLServer儲存過程
- SQL Server 資料訪問策略:儲存過程QCSQLServer儲存過程
- SQL Server資料庫遠端更新目標表資料的儲存過程SQLServer資料庫儲存過程
- SQL Server實戰六:T-SQL、遊標、儲存過程的操作SQLServer儲存過程
- Oracle儲存過程編譯卡死的解決方法Oracle儲存過程編譯
- SQL 儲存過程裡呼叫另一個儲存過程SQL儲存過程
- Oracle 編譯儲存過程卡死解決方法Oracle編譯儲存過程
- SQL Server實戰五:儲存過程與觸發器SQLServer儲存過程觸發器
- SQL Server通過建立臨時表遍歷更新資料SQLServer
- Sql Server深入的探討鎖機制SQLServer
- SQL 分頁儲存過程SQL儲存過程
- 關於SQL server2008除錯儲存過程的完整步驟SQLServer除錯儲存過程
- 使用SQL SERVER儲存過程實現歷史資料遷移SQLServer儲存過程
- 如何把SQL Server中一個表,一個儲存過程,一個檢視等改為系統表,系統儲存過程,系統檢視等...SQLServer儲存過程
- 配置SQL Server Service Broker來傳送儲存過程資料(下)SASQLServer儲存過程
- 配置SQL Server Service Broker來傳送儲存過程資料(上)CYSQLServer儲存過程
- SQL Server儲存過程模擬HTTP請求POST和GET協議SQLServer儲存過程HTTP協議
- 儲存意外斷電導致raid資訊丟失的解決過程AI
- Oracle日常問題-臨時表過多導致exp速度慢Oracle
- 關於SQL Server中儲存過程在C#中呼叫的簡單示例SQLServer儲存過程C#
- Hive SQL的底層編譯過程詳解HiveSQL編譯
- mysql 儲存過程 procedure 批次建表MySql儲存過程
- 使用Spark載入資料到SQL Server列儲存表SparkSQLServer
- MySQL儲存過程裡動態SQL的使用UXMySql儲存過程UX
- [SQL Server玩轉Python] 三.SQL Server儲存過程實現Python鳶尾花決策樹訓練及預測SQLServerPython儲存過程
- 臨時儲存程式碼
- 使用儲存過程(PL/SQL)向資料庫中儲存BLOB物件儲存過程SQL資料庫物件
- oracle的儲存過程Oracle儲存過程
- PL/SQL中動態掉用儲存過程SQL儲存過程