你了解MySQL中的多表聯合查詢嗎?


前言:

多表聯合查詢,其實就是我們MySQL中的join語句,經常會看到有人說join非常影響性能,不建議使用,你知道這是為什么呢?我們究竟可不可以用呢?

測試數據:

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;

drop procedure idata;
delimiter ;;
create procedure idata()
begin
  declare i int;
  set i=1;
  while(i<=1000)do
    insert into t2 values(i, i, i);
    set i=i+1;
  end while;
end;;
delimiter ;
call idata();

create table t1 like t2;
insert into t1 (select * from t2 where id<=100)
上述SQL創建了2個表,兩張表都有主鍵索引id,普通索引a。存儲過程是往表t2里插入1000行數據,在表t1里插入的是100行數據。
如果直接使用join語句,優化器可能會選擇表t1或者表t2作為驅動表,這樣會影響分析SQL執行過程。所以為了分析執行過程中的性能問題,我們可以使用straight_join讓MySQL使用固定的連接方式查詢,下述語句就是讓t1作為驅動表,t2作為被驅動表。
select * from t1 straight_join t2 on (t1.a =t2.a);
我們通過explain來看一下這條語句的執行結果。
這里可以看出,在這條語句匯總,被驅動表t2字段a上有索引,join過程用上了這個索引,該語句的執行流程如下;
  1. 從表t1中讀取一行數據R
  2. 從數據行R中,取出a字段到表t2里去查找
  3. 取出表t2中滿足條件的行,跟R組成一行,作為結果集的一部分
  4. 重復執行步驟1到3,直到表t1的末尾循環結束。
上述的過程和我們寫程序時的循環查找類似,並且可以使用被驅動表中的索引
在這個流程里,對驅動表t1做了全表掃描,這個過程掃描了1000行數據。由於被驅動表使用了索引,我們構造的數據都是11對應的,所以每次只掃描1行數據,總掃描也是100行,掃描總行數為200

如果不使用join會怎樣呢?

如果不使用join,那么我們需要將t1的數據全部取出,然后取出a的值,記為$R.a在執行select * from t2 where a= $R.a,再講結果和R構成結果集的一行。
這樣做雖然也是掃描了100行數據,但是總共執行了101條語句,與MySQL服務器多了100次交互,而且還需要自己去構建結果集。這么做顯然沒有直接join要好。

Simple-Nested-Loop join

在上述SQL執行的過程中,驅動表走的是全表掃描,被驅動表走的是數搜索,所以整個過程的時間復雜度可以近似表示為:O(n*log2m),所以我們應該盡量使用小表來做驅動表
當n擴大1000倍的時候,時間復雜度擴大1000倍,m擴大1000倍的時候,這個數值只擴大10不到10倍。
結論:
  1. 使用join語句,性能比強拆成多個單表執行SQL語句的性能要好
  2. 如果使用join語句的話,需要讓小表做驅動表
當然,這個結論是建立在“可以使用被驅動表的索引”的前提下的。

Block Nested-Loop Join:

這個時候,按照我們上面的分析,會不會取笛卡爾積,掃描100*1000次呢?我們可以使用explain來查看一下下面的SQL執行結果:
select * from t1 straight_join t2 on (t1.a=t2.b);
 
可以看到這里采用了一種Block Nested-Loop Join的算法。
具體的運算流程是這樣的:
  1. 把表t1的數據讀入到線程內存join_buffer中,由於我們這個語句中寫的是select * 因此會講整個表t1放入內存。
  2. 掃描表t2,把t2中的每一行數據取出來,跟join_buffer中的數據做對比,滿足join條件的,作為結果集的一部分返回。
我們可以看到,該算法的計算次數是:100*1000=10萬次。但是Block Nested-Loop Join雖然在時間復雜度上和Simple-Nested-Loop join算法一致,但是由於是內存計算,所以速度上會快很多,性能也更好。
這里還是應該選擇小表作為驅動表,因為如果驅動表太大,那么就需要將驅動表分段載入內存,將驅動表分成多少段,那么就需要掃描被驅動表多少次。所以這里還是推薦使用小表作為的驅動表。

結論:

如果可以使用被驅動表的索引,join語句非常有優勢
不能使用被驅動表的索引,只能使用Block Nested-Loop Join算法,盡量不要使用
在使用join的時候,應選擇小表作為驅動表


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM