SQL Server三種錶鏈接原理

 

  在SQL Server數據庫中,查詢優化器在處理表鏈接時,一般會使用一下三種鏈接方式:html

      • 嵌套循環鏈接(Nested Loop Join)
      • 合併鏈接 (Merge Join)
      • Hash鏈接 (Hash Join)

  充分理解這三種錶鏈接工做原理,可使咱們在優化SQL Server鏈接方面的代碼有據可依,爲開展優化工做提供必定的思路。接下來咱們來認識下這三種鏈接。算法

1. 嵌套循環鏈接(Nested Loop Join)數據庫

  該鏈接方式一般在小數據量而且語句比較簡單的場景中使用,也是比較常見的鏈接方式,好比如下示例:數組

   1:  use AdventureWorks2008 
   2:  go 
   3:  SELECT H.* 
   4:  FROM Sales.SalesOrderHeader H 
   5:  JOIN Sales.Sale
 1: use AdventureWorks2008 
sOrderDetail D
   6:  ON H.SalesOrderID=D.SalesOrderID 
   7:  WHERE H.SalesOrderID = 43659 

  AdventureWorks2008數據庫是SQL Server的一個sample,你能夠在微軟官方網站上自由下載。http://msftdbprodsamples.codeplex.com/releases/view/37109安全

  咱們在數據庫中運行這段代碼:數據結構

clip_image002

  經過執行計劃咱們能夠看到,數據庫的優化器使用了嵌套鏈接(Neasted Loops),上面第一行中的Sales.SalesOrderHeader表由於只有一行數據因此作爲外部表使用,SalesOrderDetail有12行數據作爲內部表使用。併發

  嵌套循環的工做原理如圖所示:函數

clip_image004

圖1 嵌套循環工做原理圖oop

  其原理就是根據條件從表中過濾出一個外部連接表,循環的從外部表中讀取一行數據,去內部表中進行匹配,僞碼以下:性能

For (i=0;i< Number of outerTable Row;i++)

{

    OuterTable[i] connect InnerTable[1,2.....N] To Create New Row

    WHERE OuterTable[i].data.value = OuterTable[1,2.....N].data.Value

}

  瞭解嵌套的工做原理後,咱們不難發現,這種鏈接的方式具備必定的侷限性的:

1. 由於算法是循環進行的,因此比較適合數據量較小的表進行鏈接,尤爲是外部表的數據。

2. 兩張表最好是排序的。表中的條件列和鏈接列最好有索引,尤爲是內部表必須有索引,這樣工做效率會成倍增長。

當外部表較小,而內部表較大而且鏈接字段上有索引的狀況下,循環嵌套很是高效。而且嵌套循環是三種方式中惟一支持不等式鏈接的方式。

2. 合併鏈接 (Merge Join)

  在SQL Server數據庫中,若是查詢優化器,發現要鏈接的兩張對象表,在鏈接列上都已經排序幷包含索引,那麼優化器將會極大可能選擇「合併」鏈接策略。條件是:兩個表都是排序的,而且錶鏈接條件中至少有一個等號鏈接,查詢分析器會去選擇合併鏈接。

  代碼示例:

   1:  USE AdventureWorks2008 
   2:   
   3:  GO 
   4:   
   5:  SELECT P.* 
   6:   
   7:  FROM Production.ProductModel P 
   8:   
   9:  JOIN Production.ProductModelProductDescriptionCulture PPMD 
  10:   
  11:  ON P.ProductModelID = PPMD.ProductModelID 

  根據執行計劃咱們能夠看到,此次的鏈接操做使用的合併鏈接:

clip_image006

  這兩張表中,數據量分別爲128和762行數據,鏈接列是表中的主鍵而且數據是有序的,所以數據庫的查詢優化器自動選擇了合併鏈接。合併鏈接的工做原理以下圖所示:

合併鏈接原理

圖2 合併鏈接的工做原理

  數據庫優化器在決定使用合併鏈接後,並行的在兩個表(術語叫輸入集合)中各取第一行數據,進行匹配,匹配則返回匹配行並進行鏈接。若是不匹配,那麼小的那一個表(輸入集合),則順序取下一行數據繼續嘗試匹配。

  經過其工做原理咱們能夠發現,合併鏈接能夠當作是一個相似於併發工做機制。操做分別在兩個表(輸入集合)依次獲取數據並進行比較,這就要求兩張表是有序的,有序的排列會極大的提升工做的效率。

  有關表排序的問題,若是鏈接語句中使用Sort關鍵字來排序數據表,那麼SQL Server的優化器會比較傾向於Hash Join。在合併鏈接中,並不排斥order by, group by, distinct等關鍵字,在使用這些語句時,查詢優化器也有極大的可能選擇合併鏈接。

  當咱們使用一些查詢限定條件,好比不等式(>,<,>=等)限定條件範圍,那麼合併鏈接的效率會有更好。

  合併鏈接的限定條件:

1. 兩張表的鏈接列須要排序

2. 鏈接列必須有索引

3. 哈希鏈接(Hash Join)

  當咱們嘗試將兩張數據量較大,沒有排序和索引的兩張表進行鏈接時,SQL Server的查詢優化器會嘗試使用Hash Join。

代碼示例:

   1:  SELECT * 
   2:   
   3:  FROM Production.Product P 
   4:   
   5:  Join Production.ProductSubcategory SPC 
   6:   
   7:  on P.ProductSubcategoryID = SPC.ProductSubcategoryID 

  根據執行計劃咱們能夠看到,此次的鏈接操做使用的哈希鏈接:

clip_image010

  該鏈接在處理大量無序的數據時,效率較高,可是對處理器和內存資源的消耗較大。實現過程以下:

  Hash Join鏈接的執行操做分爲兩個階段,創建和探測。

  創建是指對輸入表進行的一系列的操做。首先優化器會將輸入表中的每一行數據掃描到系統內存中,而後根據內置的散列算法計算出相應散列值,相同散列值的數據會被分到一個Hash池中。這些散列值和數據地址保存在一個Hash表中,提供給探測使用。一般優化器會選擇數據較少的表做爲創建輸入表。

  創建完成後,開始探查工做。另外一個鏈接表(咱們叫探查輸入)一樣會被逐行的掃描、計算,得出一個Hash值。鏈接操做會使用探查輸入的Hash值和創建輸入的Hash值列表進行掃描和匹配工做,最終創建鏈接。

clip_image012

  上圖是Hash鏈接的工做流程,接下來咱們能夠來了解下哈希算法的實現的機制,如下的內容是我的對算法的理解,如有偏頗請指正。

  Hash的實際含義是「散列」的意思,它主要的功能就是將一組數據,經過算法,變換成固定長度的輸出,這個輸出咱們就稱之爲散列值(Hash值),一般在安全領域,如密碼學中使用較多。

  在SQL Server裏面哈希散列函數是黑盒的,沒有具體的算法能夠參考。實際上不少開發人員在解決海量數據查詢的時候,都會採用Hash方式,而且開發適合需求的散列算法。經常使用的一些算法包括一些取餘、MD二、MD四、MD5 和 SHA-1等等。

  由於算法,不一樣的數據可能會生成相同的散列值。它將大量的數據按照規則分散到不一樣數據堆或者鏈表中,創建內部的映射關係。咱們能夠認爲他是將數組和鏈表結合在一塊兒,想要達到一種尋址容易、插入刪除方便的數據結構,而Hash表就是一種數據內容和數據存放地址之間的映射關係。

  散列函數的選擇會決定影響Hash表元數量大小和每一個鍵值包含的數據多少,這個是數學上的問題這裏不進行進一步討論。

  說到這裏,可能你們仍是不太理解,咱們這裏舉例來講明:

  好比說有兩張表:

表A{A,F,C,D,B,E……}

表B{F,B,E,D,A,F…….}

  而且表A的數據量小於表B,這兩張表進行Hash鏈接的過程以下:

Hash鏈接原理2

1. 首先數據庫會將表A中的全部數據,掃描存入內存中。

2. 內存中的表A的數據,通過散列函數依次獲得對應的散列值(Hash值)。

3. 表A中相同散列值(鍵值)的數據,會統一的放入到一個Hash池中。我的認爲Hash池中的數據,就是數組和鏈表的集合。Hash的鍵值能夠看到是一個數組的下標,而池中的數據以鏈表的形式鏈接在數組中。

Hash【鍵值】-->數據1-->數據2..............

如圖中的一組數據,數據A和數據C具備相同的Hash值,值爲001,那麼他們都被分配到以001命名的Hash池中。

4. 將Hash值和對應的數據,依次存入到一個Hash表中,創建結束。

5. 探測階段,數據庫依次讀取掃描表B中的每一行數據,並經過散列函數計算出一個Hash值。

6. 根據Hash值,去Hash表中和表A的鍵值進行匹配,找到對應的Hash池。

7. 接下來將表B的數據去和對應的Hash池中的每條數據,去對比和匹配。若是匹配成功則進行數據鏈接。

  經過對原理的瞭解,咱們能夠看到這種鏈接方式,須要大量的計算操做,對CPU帶來必定的壓力。一般Hash 鏈接操做在內存中進行,若是內存不足,數據庫會將數據寫入到硬盤中,影響性能。

    4.小結

三種鏈接方式的特色:

類型

鏈接列上索引

表的大小

排序

鏈接子句

嵌套

內部表:必須

外部表:有最好

可選

全部類型

合併

內部表:必須 聚簇索引或者覆蓋索引

外部表:必須 聚簇索引或者覆蓋索引

須要

Equi-join

HASH

內部表:不須要

外部表:可選,最好有

小的外部表,大得內部表

任意

不須要

Equi-join

三種方式對資源的壓力:

 

嵌套循環鏈接

合併鏈接

哈希鏈接

CPU

低(若是沒有顯式排序)

內存

低(若是沒有顯式排序)

IO

可能高可能低

可能高可能低

  以上是我的對三種鏈接的我的理解,不當之處請指正。

題外話:

  其實咱們能夠把這三種鏈接比喻成相親。

  嵌套鏈接就是熟人介紹,親戚朋友根據你的條件,搜索下週圍的資源,而後安排你和幾個姑娘見面,看看能不能匹配上。若是你的條件很明確(外部表索引),而且朋友對姑娘比較熟悉,對方的要求也很明確(內部表索引),那麼成功率就會比較高。

  合併鏈接就是社區或者網站組織的小型相親聯誼會,好比電影《戀愛33天中》那種8分鐘面對面的形式。男女雙方面對面進行交談(匹配判斷),每幾分鐘就換一我的再次交談,因爲你們條件和目的性明確(都有索引),因此整個流程效率會比較高。

    Hash鏈接則就像是萬人相親大會,好比上海的中山公園(條件好的已婚人士慎入)。單身青年的父母,入園後因爲各類緣由隨機的分紅各個小羣組(通過散列函數分紅Hash池)。而後參與者根據本身的判斷(確認Hash鍵值),找到合適小組後(Hash鍵值相等),依次交談交換條件和信息(嘗試匹配),看看裏面有沒有合適人選,有就進一步瞭解(匹配成功,鏈接)。

2013年11月14日 Ralf Wang

相關文章
相關標籤/搜索