前言html
前面幾篇咱們分析了關於SQL Server關於性能調優的一系列內容,我把它分爲兩個模塊。算法
第一個模塊注重基礎內容的掌握,共分7篇文章完成,內容涵蓋一系列基礎運算算法,詳細分析瞭如何查看執行計劃、掌握執行計劃優化點,並一一列舉了平常咱們日常所寫的T-SQL語句所會應用的運算符。我相信你日常所寫的T-SQL語句在這幾篇文章中都能找到相應的分解運算符。數據庫
第二個模塊注重SQL Server執行T-SQL語句的時候一些內幕解析,共分爲5篇文章完成,其中包括:查詢優化器的運行方式、運行時幾個優化指標值檢測,統計信息、利用索引等一系列內容。經過這塊內容讓咱們瞭解SQL Server爲咱們所寫的T-SQL語句如何進行優化及運行的。併發
從本篇進入第三個模塊的內容,該篇爲第一篇,該模塊主要讓咱們來指導SQL Server進行定向調整,達到優化的目的。本模塊的內容是之前面一系列內容爲前提的,但願充分掌握了前面基礎內容,方能進入本模塊內容。函數
技術準備高併發
數據庫版本爲SQL Server2012,利用微軟的之前的案例庫(Northwind)進行分析,部份內容也會應用微軟的另外一個案例庫AdventureWorks。工具
相信瞭解SQL Server的朋友,對這兩個庫都不會太陌生。post
概念理解性能
談到hint,其實概念很簡單,正如詞義理解:提示,也就是說讓咱們經過給予SQL Server提示(hint)讓數據庫運行時按照咱們的思路進行,我估計不少不怎麼了解SQL Server的童鞋都不怎麼知道,由於通常應用的很少。學習
其實,SQL Server自己的查詢優化器已經作到很好了,因此大部分狀況下不須要咱們人工干預,本身就能運行的很好,而且最大限度的優化運行項。可是,俗話說:老虎也有打盹的時候,因此,在有些場景下,就須要咱們來給數據庫指導一個方向,讓其運行的更流暢。
可是,記住了:你所應用的hint是在如今的場景中基於現有的環境下,相對是一個好的方式,不能確保你所給予的提示(Hint)永久有效,而且隨着時間推移,數據量的變動,你所發出的提示(Hint)有可能會成爲數據庫優化的絆腳石。因此沒有充分的把握不要輕易使用Hint,而且最好採用目標導向Hint。
Hint主要分爲三類應用:查詢Hint、表Hint、鏈接Hint。查詢Hint影響整個查詢,主要應用於查詢語句優化,本篇主要分析查詢Hint。
表Hint影響查詢引用的單個表,而鏈接Hint影響一個單獨的鏈接。
Hint應用方式分爲兩類:目標導向Hint和物理運算符Hint。
目標導向Hint傳遞邏輯的目標給優化器,而不會具體指定優化器應該如何達到這個目標,應該使用什麼物理運算符,或者如何排列這些運算符。因此這種運算符使咱們所推薦的,緣由很簡單:我告訴丫按照這個思路執行就能夠,至於怎麼達到,本身想辦法!這種方式從長期看對於數據庫的影響會小不少。
另一個就是物理運算符,此方式就更直接了:直接告訴丫的步驟,你按照這個去作就行。這種方式不推薦,緣由很簡單:你的思路暫時會是好的,可是過段時間就很差了。
1、查詢提示(Hint)
首先,查詢提示(Hint)是咱們在調優中應用最普遍的,由於大部分時間咱們是在調整查詢的性能。
關於查詢中的優化選項就是在指導SQL Server的鏈接類型、聚合類型、聯合類型等物理鏈接運算符。關於此塊的詳細解析,能夠參照我調優系列中前幾篇文章,分析的至關的詳細。
a、FAST N Hint提示
關於此方式的提示,我在前面的文章中已經有使用到,在介紹索引那篇文章中,能夠點擊這裏查看。
首先,這個Hint是一個目標導向hint。提示目標很簡單:告訴數據庫給我速度出前N行數據就能夠,而其它的數據你愛咋地咋地。
這個提示最優的應用環境就是:應用系統中的分頁查詢,固然其它環境能夠用。有點相似於SELECT TOP N....
其次,在咱們的應用環境中,尤爲數據量多的狀況下,若是這時候咱們的場景是:我想速度的看到前面的部分數據,其它的數據你能夠稍後再顯示,可是在執行T-SQL的時候,SQL Server會多方面的考慮耗費(cost),而後再平衡各類利弊選擇出它認爲相對好的執行計劃去執行,顯然這種方式獲取數據的方式是很浪費的,而且速度就會相對慢不少。
因此,咱們利用FAST N Hint提示,這樣,SQL Server會阻止優化器使用哈希鏈接、哈希聚合、排序、甚至是並行這些大消耗的動做,而轉變成爲這N條數據作快速的優化並輸出。這在大數據量的狀況下,是一種很是高明的方式。
來個例子:
SELECT OrderID,CustomerID,OrderDate FROM Orders ORDER BY OrderDate
簡單的查詢,而且按照OrderDate排序,不看執行計劃,咱們就已經推測出這個執行計劃中最耗損的就是這個OrderDate了,排序永遠是高耗損,這也是爲何各類類型的索引都要提早排序的緣由。
而後,咱們再來看一下加上這個FAST N Hint提示的執行
SELECT OrderID,CustomerID,OrderDate FROM Orders ORDER BY OrderDate OPTION(FAST 1)
爲了快速獲取這一行數據,利用HINT後,改成了索引掃描+書籤查找,由於這是獲取一條數據的最優的一種方式。
由於數據量的關係,因此我上述演示沒能很好的表現出FAST 提示的優越性來,其實在實際生產中,在面臨龐大的數據量的時候,通常利用FAST N提示獲取出部分數據以後,就再也不繼續運行了,由於咱們關注的就是這一部分數據。
固然,此HINT也有弊端:在快速獲取前N行結果以後,可能會延遲整個查詢的整體相應時間。也就是說,儘管FAST N HINT可能會使優化器快速產生前N個輸出計劃。可是它會使優化器產生一個在結束最後一行前花費更多時間,消耗更多CPU,甚至於更多IO。
b、OPTIMIZE FOR Hint提示
此HINT是一種很是有用的提示,也是咱們在平常中常用的。
這個HINT目標很簡單:告訴優化器目標以Hint值進行分配或者執行。此Hint提示是從SQL Server2005版本以上開始支持,可以根據指定的參數值產生一個計劃,尤爲適用於非對稱數據集中,由於這種數據集中數據分佈不均勻,不一樣的參數值可能致使不一樣的基數評估和不一樣的查詢計劃,咱們能夠從不一樣的參數中選擇一個最優的執行計劃,做爲後續不一樣參數的執行計劃,避免了SQL Server的從新評估和重編譯的耗費的動做。
來個例子:
SELECT OrderID,OrderDate FROM Orders WHERE ShipPostalCode=N'51100'
此語句很簡單,就是經過查詢郵政編碼(ShipPostalCode),獲取出訂單ID和訂單日期。
來看這個查詢語句,最理想的狀況就是直接經過索引查找(index seek)動做獲取出數據。其實最好的方式也是經過INCLUDE將兩列值包含進去。
咱們來看一下實際的執行計劃:
SQL Server經過了索引查找+書籤查找方式獲取,這種方式也湊合吧,其實咱們還能夠繼續優化。
可是,這不是問題重點,問題重點是該段T-SQL通常咱們會利用參數進行查詢或者包裝成存儲過程經過傳參調用。是吧??不會你永遠只查詢一個固定值吧....來看語句
DECLARE @ShipPostalCode NVARCHAR(50) SET @ShipPostalCode=N'51100' SELECT OrderID,OrderDate FROM Orders WHERE ShipPostalCode=@ShipPostalCode
是吧,這種方式才能作到重用嘛,不過包裝成一個存儲過程或者一個函數等,估計核心代碼確定就這樣子了。
來看看生成的執行計劃:
原本很爽的非彙集索引查找(Seek),經過我加了一個參數以後變成了彙集索引掃描(Scan)了,彙集索引掃描的性能跟表掃描基本同樣,沒有啥質的提升!
若是該表數據量特別大的話,咱們爲該語句設計的非彙集索引就失效了。只能經過依次掃描獲取數據了。有意思嗎???沒意思!!!
怎麼解決呢?這就是咱們此處提到Hint出場的時候了,告訴數據庫:丫就按照執行 「51100」 的查詢同樣去執行我傳過來的參數。
DECLARE @ShipPostalCode NVARCHAR(50) SET @ShipPostalCode=N'51100' SELECT OrderID,OrderDate FROM Orders WHERE ShipPostalCode=@ShipPostalCode OPTION(OPTIMIZE FOR( @ShipPostalCode=N'51100'))
看到了,這裏又迴歸了快速的非彙集索引查找(Seek)狀態,而且不受限制於傳過來的參數是啥。
這個提示只是告訴SQL Server查詢按照這個目標值進行操做,並不會實際影響結果值。
固然上面的問題,若是封裝成存儲過程的時候,能夠採用重編譯的方式解決,可是相比利用Hint的方式,重編譯帶來的消耗遠大的多。尤爲高併發的環境下重編譯所帶來CPU消耗是很是高的。
c、物理鏈接提示(Hint)
關於物理鏈接咱們在前面的文章中已經詳細的分析了,在SQL Server中共分爲三種物理鏈接方式:嵌套循環、合併、哈希鏈接。
詳細的內容能夠參照個人基礎篇中的連接:SQL Server調優系列基礎篇(經常使用運算符總結——三種物理鏈接方式剖析)
文章中對三種鏈接的利弊進行了詳細的對比,而且對三種鏈接的使用環境進行了詳細的介紹。可是,有時候SQL Server爲咱們評估的鏈接並非最優的,或者說並非符合咱們的要求,這時候,就要利用咱們的物理鏈接提示進行指導。
總共分爲三種查詢級別的鏈接Hint,正好對應三種物理鏈接運算符,依次是:LOOP JOIN、MERGE JOIN 和 HASH JOIN
在應用時候,能夠指定一個或者多個,若是指定一個,那麼查詢計劃中的所有鏈接使用指定的鏈接類型,若是指定兩個,SQL Server會在這兩個鏈接類型中選擇最好的一個,也就是斃掉了第三個。
應用場景蠻多的,根據三種鏈接的特性,咱們能夠有選擇的進行提示,好比咱們想一個查詢不消耗內存,那麼就能夠指定OPTION(LOOP JOIN,MERGER JOIN),這樣就去掉消耗內存的哈希鏈接,固然這是減少內存消耗但會增長執行時間。若是採用了合併鏈接(MERGER JOIN)方式不會消耗內存,可是合併鏈接須要提早排序(sort),排序會消耗大量的內存。
固然,有時候嵌套循環鏈接執行的時間不理想,就能夠指定爲哈希鏈接(hash join)進行鏈接。
來看個例子:
SELECT o.OrderID FROM Customers C JOIN Orders O ON C.CustomerID=O.CustomerID WHERE C.City=N'London'
上面的查詢計劃採用了嵌套循環的鏈接方式,兩張表依次進行循環嵌套執行。
若是,通過測試這裏發現採用合併鏈接的方式更好一點,咱們能夠採用以下Hint進行提示操做
SELECT o.OrderID FROM Customers C JOIN Orders O ON C.CustomerID=O.CustomerID WHERE C.City=N'London' OPTION(MERGE JOIN)
通過調整以後,這時候該語句就利用到了咱們設計的非彙集索引,而且由原來的索引SCAN變成了索引Seek運算。
經過以下方式,能夠指導SQL Server在哈希鏈接和合並鏈接之間作出選擇,可是必定要放棄嵌套循環鏈接。
SELECT o.OrderID FROM Customers C JOIN Orders O ON C.CustomerID=O.CustomerID WHERE C.City=N'London' OPTION(HASH JOIN,MERGE JOIN)
看以看到,通過評估SQL Server仍是依然的選擇了合併鏈接
其實,這個很正常,首先數據量不大,其次是在City列上存在非彙集索引,因此要充分利用,而且在兩張表的CustomerID是都爲索引所覆蓋,這就保證了兩張表在這列上都是預先排序(sort)了,這徹底知足了合併鏈接的條件。固然,默認選擇嵌套循環鏈接的緣由,我估計的緣由就一個:兩張表數據量不大。
固然,出來上面的HINT方式能夠指定鏈接的物理鏈接方式,還有另外更爲粗暴的一種方式,強制執行。以下:
SELECT o.OrderID FROM Customers C INNER MERGE JOIN Orders O ON C.CustomerID=O.CustomerID WHERE C.City=N'London'
固然,這種方式也手動的達到了指定採用合併鏈接的方式。
可是,此種方式有嚴重的弊端:
一、經過採用這種方式貌似暫時解決問題了,可是通過一段時間,此鏈接方式可能會嚴重阻礙數據庫的優化,而要解決此問題就不得不更改代碼。
二、只能粗暴的指定一種物理鏈接方式,不能順應SQL Server自己本身的優化策略。
上述的方式是很是不推薦的一種,大部分新手會選擇這種方式。
固然,利用Hint的方式是並不是一種萬全之策,但在當前基本能解決問題,當運行到一段週期以後,若是當前的HINT干預了SQL Server數據庫的正常運行,咱們也能夠採用適當的方式予以停用Hint。使數據庫獲得完美的平穩的正常運行。後續文章咱們依次介紹。
關於Hint這塊的使用,內容仍是挺多的,其中一部分還包含鎖提示等,後續文章咱們依次介紹,有興趣的童鞋提早關注。
其實Hint是日常咱們調優時候一種重要的工具。可是,這個工具的正確的使用則要依靠牢靠的基礎知識掌握和經驗累積。正所謂:厚積薄發! 不要輕易的看到了使用場景就妄自的進行盲目的使用。若是使用不當,還會擾亂SQL Server數據庫自己正常的生態環境,得不償失,越調越亂。
因此:施主,三思而行呀......
參考文獻
結語
此篇文章先到此吧,關於SQL Server調優工具Hint的使用還有不少內容,後續依次介紹,有興趣的童鞋能夠提早關注。
有問題能夠留言或者私信,隨時恭候有興趣的童鞋加入SQL SERVER的深刻研究。共同窗習,一塊兒進步。
文章最後給出前面幾篇的鏈接,如下內容基本涵蓋咱們平常中所寫的查詢運算的分解以及調優內容項,皆爲原創,看來有必要整理一篇目錄了.....
-----------------如下進階篇-------------------