一:Join 的問題?算法
- 在實際生產中,使用 join 通常會集中在如下兩類:sql
- DBA 不讓使用 Join ,使用 Join 會有什麼問題呢?數組
- 若是有兩個大小不一樣的表作 join,應該用哪一個表作驅動表呢?oop
二:數據準備spa
CREATE TABLE `t2` ( `id` int(11) NOT NULL, `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `a` (`a`) ) ENGINE=InnoDB; CREATE TABLE `t1` ( `id` int(11) NOT NULL, `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `a` (`a`) ) ENGINE=InnoDB;
- 創建 t1,t2 兩個徹底相同的表,t1 表中寫入 100 條數據,t2 表中 寫入 1000 條數據。線程
三:Index Nested-Loop Join(NLJ) (被驅動表有索引的狀況選擇)code
- 語句xml
- 爲了不Mysql選擇驅動表對於分析的影響,改用 straight_join 讓 MySQL 使用固定的鏈接方式執行查詢。blog
- t1 是驅動表,t2 是被驅動表。索引
- select * from t1 straight_join t2 on (t1.a=t2.a);
- 執行流程
- 在這條語句裏,被驅動表 t2 的字段 a 上有索引,join 過程用上了這個索引
- 從表 t1 中讀入一行數據 R;
- 從數據行 R 中,取出 a 字段到表 t2 裏去查找;
- 取出表 t2 中知足條件的行,跟 R 組成一行,做爲結果集的一部分;
- 重複執行步驟 1 到 3,直到表 t1 的末尾循環結束。
-
- 小結
- 這個過程是先遍歷表 t1,而後根據從表 t1 中取出的每行數據中的 a 值,去表 t2 中查找知足條件的記錄。
- 在形式上,這個過程很像寫程序時的嵌套查詢相似,而且能夠用上被驅動表的索引,因此咱們稱之爲「Index Nested-Loop Join」,簡稱 NLJ。
- 整個過程, 總掃描行數是 200(t1 200 + t2 索引樹200)
四:Block Nested-Loop Join(NLJ)(被驅動表無索引選擇)
- 語句
- select * from t1 straight_join t2 on (t1.a=t2.b);
- 因爲表 t2 的字段 b 上沒有索引,所以在執行流程時,每次到 t2 去匹配的時候,就要作一次全表掃描。
- 流程
- 把表 t1 的數據讀入線程內存 join_buffer 中,因爲咱們這個語句中寫的是 select *,所以是把整個表 t1 放入了內存;
- 掃描表 t2,把表 t2 中的每一行取出來,跟 join_buffer 中的數據作對比,知足 join 條件的,做爲結果集的一部分返回。
-
- 小結
- 能夠看到,在這個過程當中,對錶 t1 和 t2 都作了一次全表掃描,所以總的掃描行數是 1100。
- 因爲 join_buffer 是以無序數組的方式組織的,所以對錶 t2 中的每一行,都要作 100 次判斷,總共須要在內存中作的判斷次數是:100*1000=10 萬次。
- join_buffer 的大小是由參數 join_buffer_size 設定的,默認值是 256k。若是放不下表 t1 的全部數據話,策略很簡單,就是分段放。
五:總結
- 能不能使用 join ?
- 若是可使用 Index Nested-Loop Join 算法,也就是說能夠用上被驅動表上的索引,實際上是沒問題的;
- 若是使用 Block Nested-Loop Join 算法,掃描行數就會過多。
- 尤爲是在大表上的 join 操做,這樣可能要掃描被驅動表不少次,會佔用大量的系統資源。因此這種 join 儘可能不要用。
- 若是要使用 join,應該選擇大表作驅動表仍是選擇小表作驅動表?
- 在決定哪一個表作驅動表的時候,應該是兩個表按照各自的條件過濾,過濾完成以後,計算參與 join 的各個字段的總數據量,數據量小的那個表,就是「小表」,應該做爲驅動表。