在阿里巴巴的java開發手冊有這麼一條強制規定:超過三個表禁止join,須要join的字段,數據類型保持絕對一致,多表關聯查詢時,要保證被關聯的字段須要有索引。
爲何儘可能避免使用join?若是使用join,咱們應該怎麼用呢?接下來咱們就一塊兒聊一聊關於join的幾種算法。
Simple Nested-Loop Joinjava
Simple Nested-Loop Join算法是指讀取驅動表t1中的每行數據,將每行數據傳遞到被驅動表t2上,取出被驅動表t2中知足條件的行,和t1組成結果集。算法
在這個算法中,須要對t1進行全表掃描,假設t1表1000行數據,那麼須要對t2表進行1000次全表掃描,假設t2表也是1000行數據,那麼就須要掃描1000 X 1000=1000000行。sql
示例圖以下:當t1表5行數據,t2表5行數據時,須要掃描25行數據。數據庫
Index Nested-Loop Joinapache
index nested-loop join算法的優化思路是經過驅動表的匹配條件,直接與被驅動表的索引進行匹配,減小了被驅動表的掃描次數。緩存
該算法一樣要對驅動表t1進行全表掃描,可是咱們在拿着t1表的數據去被驅動表t2進行匹配時能夠利用t2表的索引,若是t1表中1000行數據,t2表中1000行數據,那麼一共就須要掃描1000+1000=2000行數據。這個過程就跟咱們寫程序時的嵌套查詢相似,而且能夠用上被驅動表的索引,因此稱之爲「Index Nested-Loop Join」,簡稱 NLJ。微信
示例以下:當t1表有5行數據,t2表有5行數據時,一共須要掃描5+5=10行數據。數據結構
Block Nested-Loop Joinoop
Block Nested-Loop join,基於塊的嵌套循環,簡稱BNL算法,其優化思路主要是減小被驅動表的循壞次數,它會將驅動表的數據緩存起來,把參與查詢的列緩存到join buffer裏,而後拿join buffer裏的數據批量與內層表的數據在join buffer中進行匹配,知足join條件的,做爲結果集的一部分返回。
能夠看到該算法對兩個表都進行了全表掃描,所以掃描的行數是兩個表的行數之和。這種場景下,雖然在掃描行數上和NLJ算法同樣,可是因爲BNL算法是在內存中進行判斷,速度上會快不少。
join buffer的大小是由參數join_buffer_size設定,默認256k。若是一次放不下驅動表的全部數據,會分段放,這種狀況下會致使被驅動表掃描屢次。若是被驅動表是冷數據表,而且屢次掃描讀取被驅動表間隔超過1S的話,就會將他放入LRU鏈表的young區域,致使業務數據沒法進入熱數據區,減小了bufferpool的命中率,這又是另一個課題了,暫不過多展開。咱們能夠經過調大join_buffer_size來提升緩存的數據量,減小對被驅動表的掃描次數。
啓用BNL算法須要在optimizer_switch參數中設置block_nested_loop=on。
BNL算法提高了join的性能,可是它在經過輔助索引鏈接後須要回表,就會消耗大量的隨機I/O,咱們知道隨機IO對MySQL的影響是很是大的。所以MySQL5.6引入了Batched Key Access(BKA,批量鍵訪問聯接)算法。
再說BKA算法時不得不提的就是MySQL的Multi-Range Read 優化,MRR的目的主要是減小磁盤的隨機訪問。咱們都知道,Innodb索引採用的是B+tree的數據結構,數據保存在主鍵索引中,而且是按照主鍵遞增的順序插入的,可是二級索引的排列順序和主鍵的排列順序通常是不同的,它保存的主鍵值也並不是按照主鍵順序排列,在回表時就會出現隨機訪問主鍵索引的狀況。因此若是能夠按照主鍵遞增順序查詢的話,對磁盤的讀比較接近順序讀,這樣就可以提高讀性能。
MRR優化的思路就是在進行範圍查詢時,在獲得主鍵值以後,先按照主鍵的順序進行排序,而後拿着排好序的主鍵ID再去主鍵索引進行查詢,這樣就能體現出順序性的優點了。由於是多值查詢,因此通常用於range、ref類型的查詢。
再說會BKA算法,當被驅動表上有索引能夠利用時,那麼就在行提交給被 join 的表以前,先對兩個表的對應列的索引字段進行join,獲得主鍵值後,按照主鍵排好序後,利用 MRR 技術,批量訪問表取數據,減小了隨機 IO。可是若是被 join 的表沒用索引的話,那就只能使用BNL算法了。
set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
MySQL在8.0版本已經實現了hash join,這裏暫不作介紹。
-
-
-
-
適當增大join_buffer_size的值,緩存的數據越多,就越能減小被驅動表掃描的次數。
-
-