MySQL Join的底層實現原理

我的名字叫一封發表於2018-11-13

mysql只支援一種join演算法:Nested-Loop Join(巢狀迴圈連線),但Nested-Loop Join有三種變種:

(注:參考公眾號:InsideMySQL)

原理:

1.Simple Nested-Loop Join

如下圖,r為驅動表,s為匹配表,可以看到從r中分別取出r1、r2、......、rn去匹配s表的左右列,然後再合併資料,對s表進行了rn次訪問,對資料庫開銷大

MySQL Join的底層實現原理

2.Index Nested-Loop Join(索引巢狀):

這個要求非驅動表(匹配表s)上有索引,可以通過索引來減少比較,加速查詢。

在查詢時,驅動表(r)會根據關聯欄位的索引進行查詢,擋在索引上找到符合的值,再回表進行查詢,也就是隻有當匹配到索引以後才會進行回表查詢。

如果非驅動表(s)的關聯健是主鍵的話,效能會非常高,如果不是主鍵,要進行多次回表查詢,先關聯索引,然後根據二級索引的主鍵ID進行回表操作,效能上比索引是主鍵要慢。

MySQL Join的底層實現原理

3.Block Nested-Loop Join:

如果有索引,會選取第二種方式進行join,但如果join列沒有索引,就會採用Block Nested-Loop Join。

可以看到中間有個join buffer緩衝區,是將驅動表的所有join相關的列都先快取到join buffer中,然後批量與匹配表進行匹配,將第一種多次比較合併為一次,降低了非驅動表(s)的訪問頻率。

預設情況下join_buffer_size=256K,在查詢的時候MySQL會將所有的需要的列快取到join buffer當中,包括select的列,而不是僅僅只快取關聯列。在一個有N個JOIN關聯的SQL當中會在執行時候分配N-1個join buffer。

MySQL Join的底層實現原理


例項:

假設兩張表a 和 b

a結構:
comments_id        bigInt(20)    P
for_comments_if    mediumint(9)
product_id         int(11)
order_id           int(11)
...複製程式碼

b結構:
id            int(11)       p
comments_id   bigInt(20)
product_id    int(11)
...複製程式碼

其中b的關聯有comments_id,所以有索引。

1.join

SELECT * FROM a gc
JOIN b gcf ON gc.comments_id=gcf.comments_id
WHERE gc.comments_id =2056複製程式碼

使用的是Index Nested-Loop Join,先對驅動表a的主鍵篩選,得到一條,然後對非驅動表b的索引進行seek匹配,預計得到一條資料。

下面這種情況沒用到索引:

SELECT * FROM a gc
JOIN b gcf ON gc.order_id=gcf.product_id複製程式碼

使用Block Nested-Loop Join,如果b表資料少,作為驅動表,將b的需要的資料快取到join buffer中,批量對a表掃描

2.left join:

SELECT * FROM a gc
LEFT JOIN b gcf ON gc.comments_id=gcf.comments_id複製程式碼

這裡用到了索引,所以會採用Index Nested-Loop Join,因為沒有篩選條件,會選擇一張表作為驅動表去進行join,去關聯非驅動表的索引。

如果加了條件

SELECT * FROM b gcf
LEFT JOIN a gc ON gc.comments_id=gcf.comments_id
WHERE gcf.comments_id =2056複製程式碼

就會從驅動表篩選出一條來進行對非驅動表的匹配。


left join:會保全左表資料,如果右表沒相關資料,會顯示null

fight join:會保全右表資料,如果左表沒相關資料,會顯示null

inner join:部分主從表,結果會取兩個錶針對on條件相匹配的最小集


相關文章