在這一期的性能調優培訓里,我想詳細談下SQL Server里計劃緩存及其副作用。在上一周你已經學到,每個提交給SQL Server的邏輯查詢會編譯成物理執行計劃。那個執行計劃然后會被緩存,即被稱為計劃緩存,用作后期的重用。首先我們來看下即席SQL語句(adhoc SQL statements,對應的反義詞:PReparedSQL statements)的副作用,即帶來的性能問題。
即席SQL語句(adhoc SQL statements)每次當你提交一個即席SQL語句到SQL Server時,對每個唯一的查詢,都會有執行計劃被編譯。“唯一的查詢”是什么意思?答案很簡單:SQL Server對完整的SQL語句(包括可能硬編碼的參數值)生成一個hash值,并使用這個hash值作為在計劃緩存里查找值。如果找到這個值的執行計劃,這個計劃就會被重用,否則的話新的計劃會被編譯并最后在計劃緩存里緩存??聪挛覀兲峤幌旅孢@3個查詢給SQL Server:
1 --清空計劃緩存 2 DBCC FREEPROCCACHE; 3 4 SELECT * FROM Sales.SalesOrderHeader 5 WHERE CustomerID = 11000 6 GO 7 8 SELECT * FROM Sales.SalesOrderHeader 9 WHERE CustomerID = 3005210 GO11 12 SELECT * FROM Sales.SalesOrderHeader13 WHERE CustomerID = 1122314 GO15 --查看緩存的執行計劃16 SELECT * FROM sys.dm_exec_cached_plans17 18 --查看對應plan_handle的對應SQL語句19 SELECT * FROM sys.dm_exec_sql_text(0x06000B0028382622B800CB0A000000000000000000000000)20 SELECT * FROM sys.dm_exec_sql_text(0x06000B00E2CE4D15B820AE0A000000000000000000000000)21 SELECT * FROM sys.dm_exec_sql_text(0x06000B005C407727B820C90A000000000000000000000000)22 SELECT * FROM sys.dm_exec_sql_text(0x06000B00B4D38136B8200908000000000000000000000000)
對這3個查詢,SQL Server會編譯3個不同的執行計劃,因為你提供硬編碼的參數值。因此計算出來的hash值在3個查詢之間是不同的,找不到被緩存的計劃。作為一個副作用,對于幾乎相同的查詢,你有3個執行計劃。這個問題被稱為計劃緩存污染(Plan Cache Pollution) 。
你用不同的執行計劃污染了你的計劃緩存,這些計劃是不能被重用的(因為硬編碼的參數值),并且你在浪費大量有用的內存,這些內存在SQL Server里可以被其他組件使用。緩存的目的應該是持續數次的高重用,特定SQL語句不屬于這個情況。
計劃穩定性如果你參數話你的SQL語句,或者使用存儲過程。在那個情況下,SQL Server可以非常容易的重用執行計劃。但是即使重用執行計劃也會帶來性能的問題。比如SQL Server為一個查詢編譯了一個需要執行書簽查找的執行計劃,因為用到的非聚集索引沒有覆蓋到查詢字段。
在第8周我們說過,如果你從表獲取少量數據,書簽查找還是有用的。當你越過臨界點時,使用全表/索引掃描將更高效。但是SQL Server如果重用緩存的執行計劃,就不會考慮這個選擇了——SQL Server只會盲目的重用你的計劃——即使性能非常糟糕!我們看看下面的實際執行計劃:
這里SQL Server盲目重用了包含書簽查找的被緩存的計劃。如你所見,估計行數(estimated number of rows)和實際行數(actual number of rows)完全不同。SQL Server基于假設那個查詢只返回1條記錄來編譯和緩存了計劃。但是實際上SQL Server返回了1499條記錄??纯磮绦杏媱?,我們會更清晰,優化器是假設只返回1條記錄才執行這個操作的。
這會導致你沒有計劃穩定性。基于估計行數,你得到書簽查找的緩存計劃,要不就是如果越過臨界點的話是全表/索引掃描。這個是我們在性能調優時經常碰到的性能問題。
如果修正這個問題呢?很簡單:通過覆蓋非聚集索引來避免書簽查找。用這個方法你就得到了計劃穩定性,不管你輸入的第一個參數是什么值都會有同樣的計劃和性能。如果你對這個問題感興趣,可以看下參數嗅探(Parameter Sniffing)(1/2)和參數嗅探(Parameter Sniffing)(2/2)。
小結在這期的性能調優培訓里,你看到計劃緩存是個雙刃劍:一方面,計劃緩存非常強大,因為你可以重用已經編譯的計劃來避免編譯成本;另一方面,它非常危險,因為定型的執行計劃,你沒有計劃穩定性,即你不能保證的性能始終如一。
希望這次培訓你有所收獲,下周我們會詳細介紹SQL Server中的重編譯。請繼續關注!
新聞熱點
疑難解答