1. 程式人生 > 實用技巧 >第六章——根據執行計劃優化效能(1)——理解雜湊、合併、巢狀迴圈連線策略...

第六章——根據執行計劃優化效能(1)——理解雜湊、合併、巢狀迴圈連線策略...

前言:

本系列文章包括:

1、理解Hash、Merge、Nested Loop關聯策略。

2、在執行計劃中發現並解決表/索引掃描。

3、介紹並在執行計劃中發現鍵查詢並解決它們。

對於效能優化,需要集中處理以下的問題:

1、為你的環境建立效能基線。

2、監控現在的效能並發現瓶頸。

3、解決瓶頸以便得到更好的效能。

一個預估執行計劃是描述查詢將會如何執行的一個藍圖,而一個實際執行計劃就是一個查詢執行時實際發生的映象。通過對比兩個執行計劃,可以發現查詢是否真的按照預估執行計劃來執行。

在執行計劃中,有一些非常重要的操作符需要清楚:

1、Join策略:SQLServer有3種策略——雜湊、合併、巢狀迴圈。每種策略都有其優缺點,本章將講述這部分。

2、掃描和查詢是SQLServer用於讀取資料的兩種方式,這兩種方式在效能優化中是核心概念。將會在下一篇中講述。

3、鍵查詢有時候會成為主要的效能問題。因為儲存引起必須從非聚集索引中跳到聚集索引,一邊找到非聚集索引中的非鍵值列的值。這樣的行為通常很耗時間。

理解雜湊、合併、巢狀迴圈連線策略

SQLServer提供了3中JOIN的策略,它們沒有絕對的好和壞之分。

1、雜湊(Hash Join):SQLServer選擇雜湊關聯作為物理操作符,一邊對於大容量資料,且未排序或者沒有索引時進行查詢。兩個程序關聯起來進行雜湊關聯,它們 為【建立】和【探測】,在【建立】程序中,會從建立輸入(即join的左表中,但是可能這個左表會在優化過程中交換位置,使得不一定就是實際上的左表。) 讀取所有行,然後在記憶體中建立一個符合關聯條件的雜湊表。在【探測】程序中,會從探測表(輸入的右表)中讀取所有的行,並根據關聯條件,與之前建立的記憶體 雜湊表匹配。

2、合併(Merge Join):如果關聯表中已經排序,SQLServer會選擇合併關聯。合併關聯要求關聯條件中最少有一個是已經被排序了的。如果資料量不大的時候,這比雜湊關聯更加有效,它並不是重負荷關聯的方式。

3、巢狀迴圈(Nested Loop):在最少兩個結果集中,使用巢狀迴圈會比較有效,這兩個結果集中,作為外部表的集合要小,而內部迴圈結果集具有有效的索引。這種方式不適用於大結果集。

準備工作:

下面將建立兩個表,然後看看各種關聯方式的執行計劃:

[sql] view plaincopyprint?

  1. USEAdventureWorks

  2. GO

  3. IFOBJECT_ID('SalesOrdHeaderDemo'

    )ISNOTNULL

  4. BEGIN

  5. DROPTABLESalesOrdHeaderDemo

  6. END

  7. GO

  8. IFOBJECT_ID('SalesOrdDetailDemo')ISNOTNULL

  9. BEGIN

  10. DROPTABLESalesOrdDetailDemo

  11. END

  12. GO

  13. SELECT*

  14. INTOSalesOrdHeaderDemo

  15. FROMSales.SalesOrderHeader

  16. GO

  17. SELECT*

  18. INTOSalesOrdDetailDemo

  19. FROMSales.SalesOrderDetail

  20. GO



步驟:

1、執行一下查詢,並開啟執行計劃(Ctrl+M):

[sql] view plaincopyprint?

  1. SELECTsh.*

  2. FROMSalesOrdDetailDemoASsd

  3. INNERJOINSalesOrdHeaderDemoASshONsh.salesorderID=sd.salesorderid

  4. GO



2、然後從執行計劃截圖中可以看到使用了雜湊連線:


3、現在先建立唯一的聚集索引在兩個表中:

[sql] view plaincopyprint?

  1. CREATEUNIQUECLUSTEREDINDEXidx_salesorderheaderdemo_SalesOrderIDONsalesordheaderdemo(SalesOrderID)

  2. GO

  3. CREATEUNIQUECLUSTEREDINDEXidx_SalesDetail_SalesOrderIDONSalesOrdDetailDemo(SalesOrderID,SalesOrderDetailID)

  4. GO



4、再次執行步驟1的語句:

5、截圖是第二次執行的執行計劃,可以發現變成了合併連線,並且表掃描變成了聚集索引掃描:


6、現在來看看巢狀迴圈關聯,在上面的查詢中新增where條件來限定查詢的結果集:

[sql] view plaincopyprint?

  1. SELECTsh.*

  2. FROMSalesOrdDetailDemoASsd

  3. INNERJOINSalesOrdHeaderDemoASshONsh.salesorderID=sd.salesorderid

  4. WHEREsh.salesorderid=43659

  5. GO




7、從執行結果中看到現在關聯變成了巢狀迴圈:

分析:

前面已經提到,雜湊關聯工作在大資料量且關聯欄位沒有排序的關聯中。所以在步驟1中,由於沒有索引或者預先排序,資料的關聯會使用雜湊關聯。

在步驟3中,建立了一個唯一的聚集索引,所以表已經通過聚集索引排序了,此時優化器會選擇合併關聯。

在步驟6中,由於使用了where條件限制資料集的大小,同時由於已經排序,所以使用了巢狀迴圈關聯。

每一種關聯方法都有其優缺點,視乎如何優化而已。有時候雜湊關聯有其非常重要的作用,但是如果可以,強烈建議每個表都應該有一個唯一的聚集索引,一邊使用 合併關聯,如果不可以,千萬別嘗試使用OPTION提示符來把關聯改成合並或者巢狀迴圈,這可能會降低效能。而巢狀迴圈僅在小結果集的時候執行的最好。


轉載於:https://blog.51cto.com/rmlifejun/1678101