爲什麼很多人不推薦mysql連表join查詢-join連表原理的幾種算法

爲什麼很多人不推薦mysql連表join查詢

join查詢是什麼?是連表查詢,我們需要兩個表的數據,就會使用join來進行連表。那麼mysql裏面是怎麼連表的呢?它和我們自己查詢出一張表的數據在遍歷去查詢另外一個表是不是一樣呢?

join查詢

join查詢還可以寫成left join,表示的是根據左邊的表來查詢右邊的表。但實際上,優化器會進行優化,選擇合適的表來做驅動表,不一定是左邊的表。

Index Nested-Loop Join

NLJ算法,這是一種基於索引的算法,如果你的連表用到了索引,則會使用這個算法。

比如下面的查詢語句:

b.uid字段有索引,並且假設a是小表,優化器選擇了a表作爲驅動表,b表作爲被驅動表。

select * from a left join b on a.id = b.uid;

執行流程大致如下:

  1. 讀取表a的第一行數據
  2. 使用表a的id去b表的索引樹上查詢到對應的b表id
  3. 通過查詢的b表id回表查詢b表數據
  4. 把a表和b表數據放在一起
  5. 重複上面的步驟直到a表遍歷完成
  6. 返回數據

可以看到這和我們自己查詢出一張表然後遍歷查詢過程是一樣的。

但是它的優勢是省略了多次連接數據庫,連接數據庫是比較耗費資源和時間的,這樣來看,使用join是更加好的選擇。當然前提是你的join被驅動表有索引。

這個過程掃描了整個表a和表a的id對應的每一行表b。假設a表的數據量是n,那麼掃描了n + n行,當然不算回表。如果算上回表其實相當於n + n + n行。

我們再看一下這個算法的時間複雜度。

b+樹中,定位一個記錄的時間複雜度大約是log(m)。m是b表的數據行數。爲什麼這裏使用了b表的數據行數而不是上面說的a表的行數呢,因爲這裏指的是b表索引樹的時間複雜度,當然是跟b表索引樹大小掛鉤也就是b表數據大小掛鉤了。如果在算上回表查詢,那麼時間複雜度大約是2log(m)。

a表要進行全表掃描,那麼a表的時間複雜度就是n,再加上每一行要去b表中查詢,那麼去b表查詢的時間就是n * 2log(m)。加起來就是n + (2nlog(m))。

這裏面2是常量可以忽略不記,而且2是因爲回表造成的,如果我們使用覆蓋索引,那麼這個2就可以去掉了。

nlog(m) 比 n小,所以顯然n的影響是最大的。也就是說n越小,那麼我們連表的速度就越快。所以我們連表的時候要使用更小的那張表作爲驅動表,然後給被驅動表的連表字段上面加上索引或者覆蓋索引。這樣我們的連表其實還是很快的。

Block Nested-Loop Join

如果你的被驅動表字段上面沒有索引,那麼mysql就會使用另外一種算法。這個算法叫BNL算法。

本來沒有索引的情況下,按照上面的流程應該是下面這樣:

  1. 讀取表a的第一行數據
  2. 使用表a的id去b表上進行全表掃描查詢到對應的b表數據
  3. 把a表和b表數據放在一起
  4. 重複上面的步驟直到a表遍歷完成
  5. 返回數據

這樣的話對於每一行表a的數據都要進行b表的全表掃描,也就是如果表a記爲n行,表b記爲m行,那麼需要掃描n * m + n行數據。和上面的n + (2nlog(m))相比,可以看到慢了多少倍。

所以mysql使用了另外一種方法,也就是BNL算法,這個算法做了一下優化,流程變成了下面這樣:

  1. 讀取表a的所有數據放入join buffer
  2. 對錶b進行全表掃描,然後把每一條數據和join buffer中的數據做對比。
  3. 把滿足條件的數據返回。

這樣的話,也就是掃描了一遍表a和表b,那麼需要掃描n + m行數據。可以看到比上面的n + (nm)來說少了n倍的掃描量。當然了,這個算法,還需要在join buffer中進行nm次對比數據,但是這樣內存判斷也要比上面的方法好很多。

同樣的這個算法對於兩張表都是全表掃描,也就無所謂上面說的需要小表做驅動表了,反正都一樣,都要全表掃描。

join buffer存在內存中,那麼他就有大小的限制,參數join_buffer_size就是限制join buffer大小的。默認值是256k。如果表a的數據大於join buffer的大小,那麼就會分段,也就是分多次進行。

但是分多次執行就會有一個問題了。比如分成了c次,那麼表b就要經歷c次的全表掃描。所以內存允許,當然Join buffer大一點速度會快一些。

這樣的話就不是上面的n + m了,而是變成了n + (c*m),c是根據join buffer大小和n的大小來決定的。如果n越小或者join buffer size越大,那麼c就越小。也就是說,如果我們使用小表作爲驅動表,那麼在遇到數據量超過join buffer的時候,速度也會比較快。

總結

總的來說,Join的時候注意下面幾點:

  • 總是用小表作爲驅動表比較好。
  • 能加索引就在被驅動表join字段上面加索引,使用NLJ算法而不是BNL算法。
  • 在沒有索引,並且驅動表數據量過大時,可以通過調大join_buffer_size的值來加速連表查詢。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章