mysql单单写join_MySQL系列之Join大法
1.?Index Nested-Loop Join
概念解釋:
假設有t1,t2兩張表,在join連接的時候,t1表驅動t2表,t1走的全部掃描,t2表使用了索引,
則這個時候join就使用了“index nested-loop join”算法,簡稱:NLJ。
NLJ執行的流程如下:
Index Nested-Loop Join 算法的執行流程
2.?Block Nested-Loop Join
基于上面t1表和t2表,join連接,t1表和t2表都沒有命中索引,都是走的全部掃描。這個
時候Join使用的是“Block Nested-Loop Join”算法,簡稱:BNL。
BNL算法的流程如下:
Block Nested-Loop Join 算法的執行流程
在explain分析語句的時候,extra附加信息中會出現“Using join buffer (Block Nested Loop)”。
join_buffer是由參數join_buffer_size決定的。默認是256k。如果放不下表 t1
的所有數據話,策略很簡單,就是分段放。
流程大概是:
取t1表的數據,放入join_buffer中,如果join buffer滿了,就掃描t2表的數據,跟join buffer
的數據進行對比,滿足join條件的作為結果集返回;清空join buffer,繼續上面的過程。
能否使用Join?
1.?如果可以使用 Index Nested-Loop Join 算法,也就是說可以用上被驅動表上的索引,
其實是沒問題的;
2.? 如果使用 Block Nested-Loop Join 算法,掃描行數就會過多。尤其是在大表上的 join
操作,這樣可能要掃描被驅動表很多次,會占用大量的系統資源。所以這種 join 盡量不
要用。
如果要使用 join,應該選擇大表做驅動表還是選擇小表做驅動表?
1. 如果是 Index Nested-Loop Join 算法,應該選擇小表做驅動表;
2.?如果是 Block Nested-Loop Join 算法:
在 join_buffer_size 足夠大的時候,是一樣的;
在 join_buffer_size 不夠大的時候(這種情況更常見),應該選擇小表做驅動表。
這個問題的結論就是,總是應該使用小表做驅動表。
注意:join慢的時候,盡量跳大join_buffer_size的值。
優化Join
1. 優化NLJ算法
Multi-Range Read 優化算法 (MRR)。這個優化的主要目的是盡量使用順序讀盤。
如果隨著輔助索引(二級索引) a 的值遞增順序查詢的話,主鍵索引id 的值就變成隨機的,
那么就會出現隨機訪問,性能相對較差。雖然“按行查”這個機制不能改,但是調整查詢的順序,
還是能夠加速的。這就是 MRR 優化的設計思路。
加入了MRR優化的執行流程如下:
1. 根據索引 a,定位到滿足條件的記錄,將 id 值放入 read_rnd_buffer 中 ;
2.?將 read_rnd_buffer 中的 id 進行遞增排序;
3. 排序后的 id 數組,依次到主鍵 id 索引中查記錄,并作為結果返回。
read_rnd_buffer 的大小是由 read_rnd_buffer_size 參數控制的。
啟用MRR算法:
set optimizer_switch="mrr_cost_based=off"
Batched Key Access (BKA) 算法
MySQL 在 5.6 引入的,是對 NLJ 算法的優化。
NLJ 算法優化后的 BKA 算法的流程如下:
Batched Key Acess 流程
啟用 BKA 算法:
set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
前兩個參數的作用是要啟用 MRR。這么做的原因是,BKA 算法的優化要依賴于MRR。
BNL 算法的優化
優化的常見做法是,給被驅動表的 join 字段加上索引,把 BNL 算法轉成 BKA 算法。
但是如果無法在被驅動表上加索引,那么:
考慮使用臨時表,使用臨時表的大致思路是:
1. 把表 t2 中滿足條件的數據放在臨時表 tmp_t 中;
2. 為了讓 join 使用 BKA 算法,給臨時表 tmp_t 的字段 b 加上索引;
3. 讓表 t1 和 tmp_t 做 join 操作。
SQL語句為:
create temporary table temp_t(id int primary key, a int, b int, index(b))engine=innodb;
insert into temp_t select * from t2 where b>=1 and b<=2000;
select * from t1 join temp_t on (t1.b=temp_t.b);
總體來看,不論是在原表上加索引,還是用有索引的臨時表,我們的思路都是讓 join 語句
能夠用上被驅動表上的索引,來觸發 BKA 算法,提升查詢性能。
上面如果不適用臨時表進行優化,那么還有其他方式進行優化?
我們可以自己實現在業務端。實現流程大致如下:
1. select * from t1;取得表 t1 的全部 1000 行數據,在業務端存入一個 hash 結構,
比如 C++ 里的 set、PHP 的數組這樣的數據結構。
2. select * from t2 where b>=1 and b<=2000; 獲取表 t2 中滿足條件的 2000 行
數據。
3. 把這 2000 行數據,一行一行地取到業務端,到 hash 結構的數據表中尋找匹配的數
據。滿足匹配的條件的這行數據,就作為結果集的一行。
關于臨時表的幾個問題:
1.? binlog_format=row,那么跟臨時表有關的語句,就不會記錄到binlog 里。只在
binlog_format=statment/mixed 的時候,binlog 中才會記錄臨時表的操作。
2. 在使用臨時表的時候,最后最好要寫上 DROP TEMPORARY TABLE,刪除臨時表
3. 臨時表只對本session會話可見,對其他的session不可見。
總結
以上是生活随笔為你收集整理的mysql单单写join_MySQL系列之Join大法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国外域名网站如何注册
- 下一篇: Linux下如何清除DNS缓存