今天我想談下性能調優培訓里的重編譯(Recompilations)。當你執行一個查詢,SQL Server里另一個變動使你執行計劃的剩余部分無效,就會發生重編譯。在那個情況下SQL Server需要保證你執行計劃的準確性,重編譯就會被觸發。重編譯會給你的SQL Server帶來額外的CPU開銷。
什么是重編譯?首先我想展示下編譯和重編譯之間的區別。2個星期前,我們討論了SQL Server里的編譯。當查詢優化器把提交的查詢轉化為實際執行計劃時,編譯就會發生。這就是說編譯在查詢執行開始前就發生。
另一方面,重編譯在查詢執行期間就會發生。因此SQL為了保證執行計劃的準確性就重編譯執行計劃的剩余部分。如果執行計劃里引用的索引在計劃執行時被刪除了。這就導致不可接受的結果。SQL Server觸發重編譯有2個類型:
我們來詳細看下這2類重編譯。當計劃不再準確,就會發生基于正確性的重編譯(Correctness-based Recompilations)。例如你的數據庫架構發生改變(新增或刪除索引,刪除統計信息),或者你的SET選項發生改變。在那個情況下,重編譯就是為了保證你計劃的準確。
如果你的統計信息發生改變,就會發生基于最優性能的重編譯(Optimality-based Recompilations)。統計信息發生改變,一方面是SQL Server會自動更新你的統計信息,另一方面是你觸發了統計信息的人為更新。那樣的情況可以是書簽查找正越過臨界點,SQL Server需要引入全表/聚集索引掃描。
我們現在再來詳細看一個在查詢執行期間,觸發很多重編譯的常見特殊情景——臨時表(Temp Tables)!
臨時表(Temp Tables)是的,你沒看錯:當你與臨時表(Temp Tables)打交道時,在SQL Server里你會引起重編譯。我們來一個非常簡單的存儲過程定義:
1 CREATE PROCEDURE DoWork 2 AS 3 BEGIN 4 CREATE TABLE #TempTable 5 ( 6 ID INT IDENTITY(1, 1) PRIMARY KEY, 7 FirstName CHAR(4000), 8 LastName CHAR(4000) 9 )10 INSERT INTO #TempTable (FirstName, LastName)11 SELECT TOP 1000 name, name FROM master.dbo.syscolumns12 SELECT * FROM #TempTable13 END14 15 GO
這個存儲過程創建了一個簡單的臨時表,往它里面插入了幾條記錄,最后從表里獲取幾條記錄。很簡單,是不是?關鍵是這個存儲過程在執行期間觸發了2個重編譯:
如何避免這2個重編譯呢?你可以使用表變量(Table Variables)代替臨時表。用表變量的話,你就不再改變數據庫架構了(它只是個變量),而且表變量是沒有統計信息的。這2個重編譯就消失了。但是當然,用表變量會引入另一個性能問題:因為它們沒有統計信息,SQL Server總是估計它們只有1行,因此你的基數預估就會完全一塌糊涂。
因此表變量在SQL Server里只有特殊使用情景:當你只和小量數據打交道時。當你和大量數據打交道時,你仍應該使用臨時表,因為它們會給你準確的統計信息,你也可以在上面建立索引。缺點就是它們會觸發重編譯。
小結今天我們討論了性能調優培訓里的重編譯(Recompilations)。如你所見,因為SQL Server需要保證你的執行計劃的準確性才會有重編譯發生。我們還看了重編譯經常發生的特殊場景——臨時表(Temp Tables)。探秘重編譯(Recompilations)(1/2)探秘重編譯(Recompilations)(2/2)
這些重編譯可以通過使用表變量來解決,但這里你也要意識到帶來的副作用。下星期我會談下SQL Server里的并行執行計劃(Parallel Execution Plans),里面會有很多有趣的事情發生。好好享受接下來的7天,到時候見!
新聞熱點
疑難解答