前言
前面我們的幾篇文章介紹了一系列關于運算符的基礎介紹,以及各個運算符的優化方式和技巧。其中涵蓋:查看執行計劃的方式、幾種數據集常用的連接方式、聯合運算符方式、并行運算符等一系列的我們常見的運算符。有興趣的童鞋可以點擊查看。
本篇介紹在SQL Server中查詢優化器的工作方式,也就是一個好的執行計劃的形成,是如何評估出來的,作為該系列的進階篇。
廢話少說,開始本篇的正題。
技術準備
數據庫版本為SQL Server2008R2,利用微軟的一個更簡潔的案例庫(Northwind)進行分析。
正文內容
在我們將寫好的一個T-SQL語句拋給SQL Server準備執行的時候,首選要經歷的過程就是編譯過程,當然如果此語句以前在SQL Server中執行過,那么將檢測是否存在已經緩存的編譯過的執行計劃,用以重用。
但是,執行編譯的過程需要執行一系列的優化過程,關于優化過程大致分為兩個階段:
1、首先,SQL Server對我們寫的T-SQL語句先執行一些簡化,通常由查詢本身來尋找交互性及重新安排操作的順序。
在此過程中,SQL Server側重于語句寫法調整,而不過多的考慮成本或者分析索引可用性的等,最重要的目標就是產生一個有效的查詢。
然后,SQL Server才會加載元數據,包括索引的統計信息,進入第二個階段。
2、在這個階段才是SQL Server一個復雜的優化過程,這個階段SQL Server會根據上一階段形成的執行計劃運算符進行評估和嘗試,甚至于重組執行計劃,所以相對這個優化過程是一個耗時的過程。
通過如下流程圖,來理解該過程:
這個圖看上去有點復雜,我們來詳細分析下,其實就是將這個優化階段分為3個子階段
<1>這個階段僅考慮串行計劃,也就說單處理器運行,如果這個階段找到了一個好的串行計劃,優化器就不會進入下一階段。所以對于數據量少的情況,或者執行語句簡單的情況下,基本采用的都是串行計劃。
當然,如果這個階段開銷比較大,那么會進入到第2個階段,再進行優化。
<2>這個階段首先對第1階段的串行計劃進行優化,然后如果環境支持并行化操作,則進行并行化操作,通過進行比較,然后進行優化后的成本如果比較低則輸出執行計劃,如果成本還是比較高,則進入第2階段,再繼續優化。
<3>其實到達這個階段就是優化的最后一個階段了,這個階段會對第2個階段中采用串行和并行的比較結果進行最后一步優化,如果串行執行好那就進一步優化,當然如果并行執行好的話,則再繼續并行優化。
其實第3階段是查詢優化器的無奈之舉,當到達第3階段了就是一個補救階段,只能最后做優化了,優化完好不好的就只能按照這個執行計劃執行了。
那么上述過程中,各個階段的優化的原則有哪些:
關于這些優化器的最重要原則的就是:盡可能的減少掃描范圍,不管是表或者索引,當然走索引比表好,索引的量也是越少越好,最理想的情況是只有一條或者幾條。
所以,SQL Server也尊重上述原則,一直圍繞著這個原則去優化。
一、篩選條件分析
所謂的篩選條件,其實就是我們所寫的T-SQL語句中的WHERE語句后面的條件,我們會通過這里面的語句進行盡量縮小數據掃描范圍,SQL Server通過這些語句來優化。
一般格式如下:
column operator <constant or variable>
或者
<constant or variable> operator column
而這上面格式中operator包括:=、>、<、=>、<=、BETWEEN、LIKE
比如:name='liudehua'、price>4000、4000<price、name like 'liu%'、name='liudehua' AND price >1000
上面這些語句是我們寫的語句中最常用的方式,并且這種方式也將被SQL Server用來減少掃描,并且這些列被索引覆蓋,那將盡量采取索引進行獲取值,但是SQL Server也不是萬能的,有些寫法它也是不能識別的,也是我們寫語句要避免的:
a、where name like '%liu'這貨就不能被SQL Server優化器識別,所以它只能通過全表掃描或者索引掃描執行。
b、name='liudehua' OR price >1000,這個同樣也是失效的,因為它不能利用兩個的篩選條件進行逐步減少掃描。
c、price+4>100這個同樣不被識別
d、name not in ('liudehua'、‘zhourunfa’),當然還有類似的:NOT 、NOT LIKE
舉個列子:
SELECT CustomerID FROM Orders WHERE CustomerID='Vinet' SELECT CustomerID FROM Orders WHERE UPPER(CustomerID)='VINET'
所以上述的方式寫語句的時候需要盡量避免,或者采取變通的方式實現。
二、索引優化
經過上面的篩選范圍的確定之后,SQL Server緊接著開始索引的選擇,首先要確定的第一件事就是篩選字段是否存在索引項,也就是說是否被索引覆蓋。
當然,如果查詢項為索引覆蓋最好,如果不被索引覆蓋,那么為了充分利用索引的特性,就引入了書簽查找(bookmark)部分。
所以,鑒于此,我們在創建索引的時候,所參考的屬性值就為篩選條件的列了。
關于利用索引優化的選擇:
CREATE INDEX EmployeesName ON Employees(FirstName,LastName) INCLUDE(HIREDATE) WITH(ONLINE=ON) GO SELECT FirstName,LastName,HireDate,EmployeeID FROM Employees WHERE FirstName='Anne'
當然也不盡然只要查詢列存在索引覆蓋就執行索引查找,這取決于掃描的內容的多少,所以對于索引的利用程度還取決獲取內容的多少
來舉個例子:
CREATE INDEX NameIndex ON person.contact(FirstName,LastName) GO SELECT * FROM Person.Contact WHERE FirstName LIKE 'K%' SELECT * FROM Person.Contact WHERE FirstName LIKE 'Y%' GO
完全相同的查詢語句,來看執行計劃:
完全相同的查詢語句,產生的查詢計劃完全不同,一個是索引掃描,一個則是高效的索引查找。
這里我只告訴你:FirstName like 'K%'的有1255行;而FirstName like 'Y%'只有37行,其中
其實,關于這里的原因就是統計信息在作怪了。
所以,特定的T-SQL語句不一定生成特定的查詢計劃,同樣特定的查詢計劃不一定是最優的方式,影響的它的因素很多:關于索引、關于硬件、關于表內容、關于統計信息等諸多因素影響。
關于統計信息這塊是大篇幅內容,我們放在以后的篇幅中介紹,有興趣的可以提前關注。
有問題可以留言或者私信,隨時恭候有興趣的童鞋加入SQL SERVER的深入研究。共同學習,一起進步。
文章最后給出前面幾篇的連接,以下內容基本涵蓋我們日常中所寫的查詢運算的分解,看來有必要整理一篇目錄了.....
如果您看了本篇博客,覺得對您有所收獲,請不要吝嗇您的“推薦”。
文章列表
留言列表