1. 程式人生 > >MySQL查詢優化之NLJ和BNL

MySQL查詢優化之NLJ和BNL

原文地址:https://dev.mysql.com/doc/refman/5.7/en/nested-loop-joins.html

譯文:

8.2.1.6 巢狀迴圈連線演算法

MySQL使用巢狀迴圈演算法或其變體執行表之間的連線。

巢狀迴圈連線(NLJ)演算法

一個簡單的nested-loop join (NLJ)演算法每次讀取一個迴圈中的第一個表中的行,將每一行傳遞給一個巢狀迴圈,該迴圈處理連線中的下一個表。只要還有表需要連線,這個過程就會重複多次。

假設三個表t1、t2和t3之間的連線將使用以下連線型別執行:

Table   Join Type
t1      range
t2      ref
t3      ALL

如果使用了NLJ演算法,連線會被處理成如下形式:

for each row in t1 matching range {
  for each row in t2 matching reference key {
    for each row in t3 {
      if row satisfies join conditions, send to client
    }
  }
}

由於NLJ演算法每次將一行從外部迴圈傳遞到內部迴圈,因此它通常會多次讀取在內部迴圈中處理的表。

塊巢狀迴圈連線(BNL)演算法

BNL演算法通過使用外部迴圈中讀取行的緩衝,來減少內部迴圈中的表必須被讀取的次數。例如,如果將10行讀入緩衝區,並將緩衝區傳遞給下一個內部迴圈,則可以將內部迴圈中讀取的每一行與緩衝區中的所有10行進行比較。這樣可以將必須讀取內部表的次數減少一個數量級。

MySQL連線緩衝具有如下特性:

    1)當連線型別為all或index(換句話說,當不能使用可能的索引鍵時,將分別對資料行或索引行進行全面掃描)或range時,可以使用連線緩衝。緩衝區的使用同樣適用於外連結,可以參考:Section 8.2.1.11, “Block Nested-Loop and Batched Key Access Joins”

    2)永遠不會為第一個非常量表分配連線緩衝區,即使連線是all型別或index型別;

    3)只有與聯接相關的列儲存在聯接緩衝區中,而不是所有的行;

    4)系統變數join_buffer_size決定了每個用於處理查詢的連線緩衝區的大小;

    5)每個可以緩衝的連線都會被分配一個緩衝區,所以可能使用多個連線緩衝區處理給定的查詢;

    6)連線緩衝區在執行連線之前分配,在查詢完成後釋放。

例如對於前文使用NLJ演算法(沒有緩衝區)的連線,使用緩衝後連線被處理成如下形式:

for each row in t1 matching range {
  for each row in t2 matching reference key {
    store used columns from t1, t2 in join buffer
    if buffer is full {
      for each row in t3 {
        for each t1, t2 combination in join buffer {
          if row satisfies join conditions, send to client
        }
      }
      empty join buffer
    }
  }
}

if buffer is not empty {
  for each row in t3 {
    for each t1, t2 combination in join buffer {
      if row satisfies join conditions, send to client
    }
  }
}

如果S是在儲存在連線緩衝區中的t1和t2組合的大小,C為緩衝區中組合的個數,則掃描表t3的次數為:

(S * C)/join_buffer_size + 1

t3表掃描的次數隨著join_buffer_size的值的增加而減少,直到join_buffer_size足夠大以容納以前所有的行組合時為止。在這種情況下,使join_buffer_size變大並不能提高速度。

PS:由於水平有限,譯文中難免存在謬誤,歡迎批評指正。