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:由於水平有限,譯文中難免存在謬誤,歡迎批評指正。