SQL Server SQL效能優化之引數化

發表於2016-08-29

資料庫引數化的模式

資料庫的引數化有兩種方式,簡單(simple)和強制(forced),預設的引數化預設是“簡單”,簡單模式下,如果每次發過來的SQL,除非完全一樣,否則就重編譯它(特殊情況會自動引數化,正是本文想說的重點)

強制模式就是將adhoc SQL強制引數化,避免每次執行的時候因為引數值的不同而重編譯,這裡不詳細說明。

這首先要感謝“瀟湘隱者”大神的提示,當時也是遇到一個實際問題,發現執行計劃對資料行的預估,怎麼都不對,有觀察到無論怎麼改變引數,SQL語句執行前都沒有重編譯,疑惑了好一會,這個問題正是簡單引數化模式下,對某些SQL自動引數化造成執行計劃重用引起的,也是本文想表達的重點。

這個問題之前就寫過,當時也只是看書上理論上這麼說的,沒有想到其帶來的影響

該引數是一個資料級別的選項,設定情況可以參考下圖

什麼情況下會自動引數化

簡單引數化模式下,對於有且只有一種執行方式的Adhoc SQL語句,SQL Server會自動引數化它,從而達到重用執行計劃的目的。

究竟哪些型別的SQL會被自動引數化,後面會舉例說明。

自動引數化會存在哪些問題

在簡單模式下,SQL對於某些SQL會自動引數化他,避免每次都重編譯。

SQL Server 自動引數化SQL語句的行為,能夠避免一些重編譯,原本也是出於“好意”,但是這種“好意”往往不一定總是給我們帶來好處。

舉例說明什麼情況下會自動引數化

先造一個簡單的測試環境

之所以自動引數化了SQL語句,就是因為select * from TestAuotParameter where id=33333 (66666,99999)這句SQL語句,在當前的資料量下和唯一索引的特點,決定了有且只有一種高效的執行方式(也就是索引查詢)

這裡說有且只有一種方式是表中資料量相對較多,又因為idx_id這個索引是unique的。如果不是unique的,那麼情況就不同了,下面來解釋什麼是有且只有一種高效的執行計劃

如下截圖:同樣的測試,我刪除id上的唯一索引,建立為非唯一索引,再做同樣的測試,就會發現執行同樣的SQL並沒有被自動引數化

這裡解釋一下原因,索引型別怎麼跟執行計劃快取扯上了?

對於非唯一索引,有可能作做引查詢是高效的,有可能做全表掃描是高效的(比如某個ID的資料分佈的特別多)此時執行計劃有可能是多樣的,不僅僅只有一種方式,所以就不會自動引數化SQL

自動引數化存在的問題

自動引數化好處並不用多說,因為可以重用快取的執行計劃,避免了每次引數值不一樣就重編譯的問題。說到執行計劃重用,不得不說的一個話題就是parameter sniff,嘴皮子都磨破的問題了

沒錯,自動引數化因為不同引數會重用第一次編譯生成的執行計劃,很有可能造成parameter sniff問題,以及parameter sniff衍生出來的其他問題

同樣用一個例子來做演示,該問題是最近在觀察執行計劃統計資訊(statistics)預估問題時遇到的一個問題,讓我困惑了好一會,這裡再次感謝瀟湘隱者。

該問題同樣也是因為自動引數化了SQL語句,造成執行計劃重用,從而造成一個極其簡單的SQL執行效率在某些情況下較低的情況,為什麼自動化引數的原因跟上述類似,都是有且只有一種執行方式(索引查詢)的情況下,不同引數執行計劃重用造成對資料行的錯誤預估。測試之前清空一下快取執行計劃,觀察不同查詢條件下的實際執行計劃對資料行的預估

如下查詢條件:

1,初始查詢條件為:CreateDate>’2016-6-1′ and CreateDate

2,將查詢條件更新為:CreateDate>’2016-6-1′ and CreateDate

3,將查詢條件更新為:CreateDate>’2016-6-1′ and CreateDate

發現沒有,因為查詢時間段有變化,實際行數也有變化,但是不管實際行數多少,預估行數總是為第一次執行預估的行數。

這肯定不對吧?隨便帶入什麼條件,預估行數都是37117,當時一下子蒙了,怎麼每次執行SQL對資料行的預估都是一樣的?

其實這個問題跟一開始舉例的一樣,都是SQL語句被自動引數化了,因此造成了執行計劃重用,執行計劃重用,導致錯誤地預估實際查詢的資料行數。

如何解決自動引數化造成錯誤地重用執行計劃的問題

很多問題找到了真正的原因,解決起來往往並不難,這個問題的原因是執行計劃重用造成的,那麼我們只需要解決執行計劃重用的問題即可。也就是不讓他重用執行計劃,只需要在SQL語句中加一個提示即可,也即:select COUNT(1) from Test20160810 where CreateDate>’2016-6-1′ and CreateDateOPTION(RECOMPILE)

原因就在於加上OPTION(RECOMPILE)這個查詢提示之後,不快取SQL的執行計劃快取,沒有了執行計劃快取,也就沒得重用了

比如這個查詢,在查詢語句中加入OPTION(RECOMPILE)查詢提示,讓其執行之前重編譯SQL語句,他就可以正確地預估資料行了。

總結

本文通過一個實際案例說明了什麼是簡單引數模式下的自動化引數,自動化引數會帶來哪些問題,以及如何解決,問題本身非常簡單,如果不注意還是會偶爾還是會出現困惑的。

題外話

有一點感受非常深,就是說,越來越多的實際問題,都要有理論知識作支撐,但往往理論上說的情況並沒有頻繁出現或者即使出現了也沒有引起注意,有時間就忽略了一些理論上的知識。

對於遇到的問題,如果真的要想弄清楚,還是要有一些理論知識做鋪墊的。很多時候,往往是遇到問題之後,回憶起來曾經好似乎看過這一方面的理論知識。這也是我們需要堅持看書,瞭解一些理論知識的原因。

相關文章