mysql只支援一種join演算法:Nested-Loop Join(巢狀迴圈連線),但Nested-Loop Join有三種變種:
(注:參考公眾號:InsideMySQL)
原理:
1.Simple Nested-Loop Join:
如下圖,r為驅動表,s為匹配表,可以看到從r中分別取出r1、r2、......、rn去匹配s表的左右列,然後再合併資料,對s表進行了rn次訪問,對資料庫開銷大
2.Index Nested-Loop Join(索引巢狀):
這個要求非驅動表(匹配表s)上有索引,可以通過索引來減少比較,加速查詢。
在查詢時,驅動表(r)會根據關聯欄位的索引進行查詢,擋在索引上找到符合的值,再回表進行查詢,也就是隻有當匹配到索引以後才會進行回表查詢。
如果非驅動表(s)的關聯健是主鍵的話,效能會非常高,如果不是主鍵,要進行多次回表查詢,先關聯索引,然後根據二級索引的主鍵ID進行回表操作,效能上比索引是主鍵要慢。
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。
例項:
假設兩張表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條件相匹配的最小集