Mysql JOIN原理

轉載地址:https://www.cnblogs.com/shengdimaya/p/7123069.html
在寫sql語句時,常用到JOIN多個關聯表,那麼到底它的原理是什麼呢,看到有些不錯的文字?自己也來做下分析
MySQL是隻支持一種JOIN算法Nested-Loop Join(嵌套循環鏈接),不像其他商業數據庫可以支持哈希鏈接和合並連接,不過MySQL的Nested-Loop Join(嵌套循環鏈接)也是有很多變種,能夠幫助MySQL更高效的執行JOIN操作:
(1)Simple Nested-Loop Join(圖片爲InsideMySQL取來)
這個算法相對來說就是很簡單了,從驅動表中取出R1匹配S表所有列,然後R2,R3,直到將R表中的所有數據匹配完,然後合併數據,可以看到這種算法要對S表進行RN次訪問,雖然簡單,但是相對來說開銷還是太大了
(2)Index Nested-Loop Join,實現方式如下圖:
索引嵌套聯繫由於非驅動表上有索引,所以比較的時候不再需要一條條記錄進行比較,而可以通過索引來減少比較,從而加速查詢。這也就是平時我們在做關聯查詢的時候必須要求關聯字段有索引的一個主要原因。
這種算法在鏈接查詢的時候,驅動表會根據關聯字段的索引進行查找,當在索引上找到了符合的值,再回表進行查詢,也就是隻有當匹配到索引以後纔會進行回表。至於驅動表的選擇,MySQL優化器一般情況下是會選擇記錄數少的作爲驅動表,但是當SQL特別複雜的時候不排除會出現錯誤選擇。
在索引嵌套鏈接的方式下,如果非驅動表的關聯鍵是主鍵的話,這樣來說性能就會非常的高,如果不是主鍵的話,關聯起來如果返回的行數很多的話,效率就會特別的低,因爲要多次的回表操作。先關聯索引,然後根據二級索引的主鍵ID進行回表的操作。這樣來說的話性能相對就會很差。
(3)Block Nested-Loop Join,實現如下:
在有索引的情況下,MySQL會嘗試去使用Index Nested-Loop Join算法,在有些情況下,可能Join的列就是沒有索引,那麼這時MySQL的選擇絕對不會是最先介紹的Simple Nested-Loop Join算法,而是會優先使用Block Nested-Loop Join的算法。
Block Nested-Loop Join對比Simple Nested-Loop Join多了一箇中間處理的過程,也就是join buffer,使用join buffer將驅動表的查詢JOIN相關列都給緩衝到了JOIN BUFFER當中,然後批量與非驅動表進行比較,這也來實現的話,可以將多次比較合併到一次,降低了非驅動表的訪問頻率。也就是只需要訪問一次S表。這樣來說的話,就不會出現多次訪問非驅動表的情況了,也只有這種情況下才會訪問join buffer。
在MySQL當中,我們可以通過參數join_buffer_size來設置join buffer的值,然後再進行操作。默認情況下join_buffer_size=256K,在查找的時候MySQL會將所有的需要的列緩存到join buffer當中,包括select的列,而不是僅僅只緩存關聯列。在一個有N個JOIN關聯的SQL當中會在執行時候分配N-1個join buffer。
上面介紹完了,下面看一下具體的列子
(1)全表JOIN
EXPLAIN SELECT * FROM comments gc
JOIN comments_for gcf ON gc.comments_id=gcf.comments_id;

 

看一下輸出信息:
 
可以看到在全表掃描的時候comments_for 作爲了驅動表,此事因爲關聯字段是有索引的,所以對索引idx_commentsid進行了一個全索引掃描去匹配非驅動表comments ,每次能夠匹配到一行。此時使用的就是Index Nested-Loop Join,通過索引進行了全表的匹配,我們可以看到因爲comments_for 表的量級遠小於comments ,所以說MySQL優先選擇了小表comments_for 作爲了驅動表。
(2)全表JOIN+篩選條件
SELECT * FROM comments gc
JOIN comments_for gcf ON gc.comments_id=gcf.comments_id
WHERE gc.comments_id =2056

 

此時使用的是Index Nested-Loop Join,先對驅動表comments 的主鍵進行篩選,符合一條,對非驅動表comments_for 的索引idx_commentsid進行seek匹配,最終匹配結果預計爲影響一條,這樣就是僅僅對非驅動表的idx_commentsid索引進行了一次訪問操作,效率相對來說還是非常高的。
(3)看一下關聯字段是沒有索引的情況:
EXPLAIN SELECT * FROM comments gc
JOIN comments_for gcf ON gc.order_id=gcf.product_id

 

我們看一下執行計劃:
從執行計劃我們就可以看出,這個表JOIN就是使用了Block Nested-Loop Join來進行表關聯,先把comments_for (只有57行)這個小表作爲驅動表,然後將comments_for 的需要的數據緩存到JOIN buffer當中,批量對comments 表進行掃描,也就是隻進行一次匹配,前提是join buffer足夠大能夠存下comments_for的緩存數據。
而且我們看到執行計劃當中已經很明確的提示:Using where; Using join buffer (Block Nested Loop)
一般情況出現這種情況就證明我們的SQL需要優化了。
要注意的是這種情況下,MySQL也會選擇Simple Nested-Loop Join這種暴力的方法,我還沒搞懂他這個優化器是怎麼選擇的,但是一般是使用Block Nested-Loop Join,因爲CBO是基於開銷的,Block Nested-Loop Join的性能相對於Simple Nested-Loop Join是要好很多的。
(4)看一下left join
EXPLAIN SELECT * FROM comments gc
LEFT JOIN comments_for gcf ON gc.comments_id=gcf.comments_id

 

看一下執行計劃:
這種情況,由於我們的關聯字段是有索引的,所以說Index Nested-Loop Join,只不過當沒有篩選條件的時候會選擇第一張表作爲驅動表去進行JOIN,去關聯非驅動表的索引進行Index Nested-Loop Join。
如果加上篩選條件gc.comments_id =2056的話,這樣就會篩選出一條對非驅動表進行Index Nested-Loop Join,這樣效率是很高的。
如果是下面這種:
EXPLAIN SELECT * FROM comments_for gcf
LEFT JOIN comments gc ON gc.comments_id=gcf.comments_id
WHERE gcf.comments_id =2056


通過gcf表進行篩選的話,就會默認選擇gcf表作爲驅動表,因爲很明顯他進行過了篩選,匹配的條件會很少,具體可以看下執行計劃:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章