轉載自:https://zhuanlan.zhihu.com/p/54275505
了解join 算法原理之前你可能還需要了解:
mysql索引原理:勤勞的小手:平衡二叉樹、B樹、B+樹、B*樹 理解其中一種你就都明白了
Simple Nested-Loop Join(簡單的嵌套循環連接)
簡單來說嵌套循環連接算法就是一個雙層for 循環 ,通過循環外層表的行數據,逐個與內層表的所有行數據進行比較來獲取結果,當執行select * from user tb1 left join level tb2 on tb1.id=tb2.user_id
時,我們會按類似下面代碼的思路進行數據匹配:
整個匹配過程會如下圖:
特點:
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 為索引的時候執行過程會如下圖:
注意:使用Index Nested-Loop Join 算法的前提是匹配的字段必須建立了索引。
Block Nested-Loop Join(緩存塊嵌套循環連接)
Block Nested-Loop Join 其優化思路是減少外層表的循環次數,Block Nested-Loop Join 通過一次性緩存多條數據,把參與查詢的列緩存到join buffer 里,,然后拿join buffer里的數據批量與內層表的數據進行匹配,從而減少了外層循環的次數,當我們不使用Index Nested-Loop Join的時候,默認使用的是Block Nested-Loop Join。
案例:
如SQL:select * from user tb1 left join level tb2 on tb1.id=tb2.user_id
當level 表的 user_id 不為索引的時候執行過程會如下圖:
注意:
1、使用Block Nested-Loop Join 算法需要開啟優化器管理配置的optimizer_switch的設置block_nested_loop為on 默認為開啟,如果關閉則使用Simple Nested-Loop Join 算法;
通過指令:Show variables like 'optimizer_switc%'; 查看配置
2、設置join buffer 的大小
通過join_buffer_size參數可設置join buffer的大小
指令:Show variables like 'join_buffer_size%';
Join 算法總結
不論是Index Nested-Loop Join 還是 Block Nested-Loop Join 都是在Simple Nested-Loop Join
的算法的基礎上 減少嵌套的循環次數, 不同的是 Index Nested-Loop Join 是通過索引的機制減少內層表的循環次數,Block Nested-Loop Join 是通過一次緩存多條數據批量匹配的方式來減少外層表的循環次數,通過 理解join 的算法原理我們可以得出以下表連接查詢的優化思路。
1、永遠用小結果集驅動大結果集(其本質就是減少外層循環的數據數量)
2、為匹配的條件增加索引(減少內層表的循環次數)
3、增大join buffer size的大小(一次緩存的數據越多,那么外層表循環的次數就越少)
4、減少不必要的字段查詢(字段越少,join buffer 所緩存的數據就越多,外層表的循環次數就越少)