Mysql中的Join詳解


一、Simple Nested-Loop Join(簡單的嵌套循環連接)

簡單來說嵌套循環連接算法就是一個雙層for 循環 ,通過循環外層表的行數據,逐個與內層表的所有行數據進行比較來獲取結果,當執行select * from user tb1 left join level tb2 on tb1.id=tb2.user_id

時,我們會按類似下面代碼的思路進行數據匹配:

img

整個匹配過程會如下圖:

img

特點:

Nested-Loop Join 簡單粗暴容易理解,就是通過雙層循環比較數據來獲得結果,但是這種算法顯然太過於粗魯,如果每個表有1萬條數據,那么對數據比較的次數=1萬 * 1萬 =1億次,很顯然這種查詢效率會非常慢。

當然mysql 肯定不會這么粗暴的去進行表的連接,所以就出現了后面的兩種對Nested-Loop Join 優化算法,在執行join 查詢時mysql 會根據情況選擇 后面的兩種優join優化算法的一種進行join查詢。


二、Index Nested-Loop Join(索引嵌套循環連接)

Index Nested-Loop Join其優化的思路 主要是為了減少內層表數據的匹配次數, 簡單來說Index Nested-Loop Join 就是通過外層表匹配條件 直接與內層表索引進行匹配,避免和內層表的每條記錄去進行比較, 這樣極大的減少了對內層表的匹配次數,從原來的匹配次數=外層表行數 * 內層表行數,變成了 外層表的行數 * 內層表索引的高度,極大的提升了 join的性能。

案例:

如SQL:select * from user tb1 left join level tb2 on tb1.id=tb2.user_id

當level 表的 user_id 為索引的時候執行過程會如下圖:

img

注意:使用Index Nested-Loop Join 算法的前提是匹配的字段必須建立了索引。


三、Block Nested-Loop Join(緩存塊嵌套循環連接)

Block Nested-Loop Join 其優化思路是減少內層表的掃表次數,通過簡單的嵌套循環查詢的圖,我們可以看到,左表的每一條記錄都會對右表進行一次掃表,掃表的過程其實也就是從內存讀取數據的過程,那么這個過程其實是比較消耗性能的。

img

所以緩存塊嵌套循環連接算法意在通過一次性緩存外層表的多條數據,以此來減少內層表的掃表次數,從而達到提升性能的目的。如果無法使用Index Nested-Loop Join的時候,數據庫是默認使用的是Block Nested-Loop Join算法的

當level 表的 user_id 不為索引的時候,默認會使用Block Nested-Loop Join算法,匹配的過程類似下圖。

img

注意:

1、使用Block Nested-Loop Join 算法需要開啟優化器管理配置的optimizer_switch的設置block_nested_loop為on 默認為開啟,如果關閉則使用Simple Nested-Loop Join 算法;

通過指令:Show variables like 'optimizer_switc%'; 查看配置

img

2、設置join buffer 的大小

通過join_buffer_size參數可設置join buffer的大小

指令:Show variables like 'join_buffer_size%';

img


四、Merge Join

sort merge-join,merge join需要首先對兩個表按照關聯的字段進行排序;排序合並連接原理是先對兩個表/行源根據JOIN列進行全掃描后排序,然后再進行連接。排序合並連接可以處理非等值JOIN。排序合並連接非常耗費資源,主因是對要連接的表/結果集進行排序,通常情況下,CBO是不會優先選擇該表連接方式。
示例:select * from a,b where a.id<=b.id;

注意:可以使用USE_MERGE(table_name1 table_name2)來強制使用排序合並連接。Sort Merge join 用在沒有索引,並且數據已經排序的情況。通常走Sort Merge join的場景:

  1. RBO模式
  2. 不等價關聯(>,<,>=,<=,<>)
  3. HASH_JOIN_ENABLED=false
  4. 數據源已排序

五、Hash Join

Hash Join (WL#2241) 此功能由 Erik Froseth 實現,為 MySQL 中執行內部等價聯接的一種方式。例如:SELECT*FROM t1 JOIN t2 ON t1.col1=t2.col1;可以在 8.0.18 中作為 Hash Join 執行。Hash Join 不需要任何索引來執行,並且在大多數情況下比當前的塊嵌套循環算法更有效。
示例如下:


根據MySQL官方文檔介紹,Hash Join不需要任何索引來執行,並且在大多數情況下比當前的塊嵌套循環算法更有效。下面介紹下啥是Hash Join。

​ hash join是一種數據庫在進行多表連接時的處理算法,對於多表連接還有兩種比較常用的方式:sort merge-join 和 nested loop。大家都知道,oracle,postgresql都已支持hash-join,mysql8.0.17之前版本是不支持的。hash-join本身的實現並不是很復雜,但需要優化器的實現配合才能最難的地方。

​ 多表連接分為以下幾種:內連接(inner join),外連接(inner join)和交叉連接。外連接又分為:左外連接,右外連接和全外連接。對於不同的查詢方式,使用相同的join算法也會有不同的代價產生,具體使用哪一種連接方式是由優化器通過代價的衡量來決定。
​ hash-join是CBO 做大數據集連接時的常用方式,對於兩個表來講,優化器使用兩個表中較小的表利用連接鍵在內存中建立hash表,然后掃描較大的表,找出與hash表中匹配的行。因hash表是放在內存中,所以可很快的得到對應的hash表與較大表相匹配的行。

​ 這種方式適用於較小的表完全可以放於內存中的情況,總成本就是訪問兩個表的成本之和。但如果HASH表太大,無法一次構造在內存中,這時優化器會將它分割成若干不同的partition,不能放入內存的部分就把該分區寫入磁盤的temporary segment,此時要有較大的臨時段從而盡量提高I/O 的性能;因會多一個寫的代價,會降低效率。

​ 可以用USE_HASH(table_name1 table_name2)提示來強制使用散列連接。


六、優化器

好了,接下來要講一講優化器了。

大家都知道,優化器的處理是比較復雜,也是sql執行過程張最難的地方,優化器沒有最優只有更優。目前的優化器大都屬於CBO(COST-BASED OPTIMIZER)類型。優化器的路徑選擇會依據一些統計信息,如列最大值、最小值、直方圖、DISTINCT值等,找出消耗CPU和內存最少的sql執行路徑。

優化器作用在於路徑選擇,多表連接如何確定表連接的順序和連接方式;不同的數據庫有着不同的選擇,pg支持動態規划算法,表數量過多的時候使用遺傳算法。

這里先看下hash join的實現步驟:
1.hash join的實現分為build table也就是被用來建立hash map的小表和probe table,hash join本身的實現不要去判斷哪個是小表,優化器生成執行計划時就已經確定了表的連接順序;
2.首先依次讀取小表的數據,對於每一行數據根據連接條件生成一個hash map中的一個元組;
3.數據緩存在內存中,如內存放不下需dump到外存temporary segment上;
4.依次掃描探測表拿到每一行數據根據join condition生成hash key映射hash map中對應的元組,元組對應的行和探測表的這一行有着同樣的hash key, 這時並不能確定這兩行就是滿足條件的數據,需要再次過一遍join condition和filter,滿足條件的數據集返回需要的投影列。

Hash join的代價值:
cost = (outer access cost * # of hash partitions) + inner access cost+JOIN_CONDITION_COST + FILTER_COST

注意:
hash table的大小、需要分配多少個patition是一個問題,分配太大會造成內存浪費,分配太小會導致一旦超過內存限制,會dump到外存,不同數據庫有不同的實現方式。

小結:

  1. Hash join通常將相對小的那個表做hash運算,將關聯列數據存儲到hash列表中,從另一個表中抽取記錄,做hash運算,到hash 列表中找到相應的值,做關聯比對。
  2. Nested loops 是從一張表中讀取數,訪問另一張表來做匹配(關聯屬性通常要建index),適用場合是關聯表較小時,效率會更高。
  3. Merge Join 是先將關聯表的關聯列各自先行做排序,從各自的排序表中抽取數據,到另一個排序表中做匹配。merge join的主要耗時體現在關聯了的排序上,故消耗的資源更多。

七、Join 算法總結

不論是Index Nested-Loop Join 還是 Block Nested-Loop Join 都是在Simple Nested-Loop Join的算法的基礎上進行優化,這里 Index Nested-Loop Join 和Nested-Loop Join 算法是分別對Join過程中循環匹配次數和IO 次數兩個角度進行優化。

Index Nested-Loop Join 是通過索引的機制減少內層表的循環匹配次數達到優化效果,而Block Nested-Loop Join 是通過一次緩存多條數據批量匹配的方式來減少內層表的掃表IO次數,通過 理解join 的算法原理我們可以得出以下表連接查詢的優化思路。

1、永遠用小結果集驅動大結果集(其本質就是減少外層循環的數據數量)

2、為匹配的條件增加索引(減少內層表的循環匹配次數)

3、增大join buffer size的大小(一次緩存的數據越多,那么內層包的掃表次數就越少)

4、減少不必要的字段查詢(字段越少,join buffer 所緩存的數據就越多)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM