前言:
多表聯合查詢,其實就是我們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過程用上了這個索引,該語句的執行流程如下;
- 從表t1中讀取一行數據R
- 從數據行R中,取出a字段到表t2里去查找
- 取出表t2中滿足條件的行,跟R組成一行,作為結果集的一部分
- 重復執行步驟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倍。
結論:
- 使用join語句,性能比強拆成多個單表執行SQL語句的性能要好
- 如果使用join語句的話,需要讓小表做驅動表
當然,這個結論是建立在“可以使用被驅動表的索引”的前提下的。
Block Nested-Loop Join:
這個時候,按照我們上面的分析,會不會取笛卡爾積,掃描100*1000次呢?我們可以使用explain來查看一下下面的SQL執行結果:
select * from t1 straight_join t2 on (t1.a=t2.b);


可以看到這里采用了一種Block Nested-Loop Join的算法。
具體的運算流程是這樣的:
- 把表t1的數據讀入到線程內存join_buffer中,由於我們這個語句中寫的是select * 因此會講整個表t1放入內存。
- 掃描表t2,把t2中的每一行數據取出來,跟join_buffer中的數據做對比,滿足join條件的,作為結果集的一部分返回。
我們可以看到,該算法的計算次數是:100*1000=10萬次。但是Block Nested-Loop Join雖然在時間復雜度上和Simple-Nested-Loop join算法一致,但是由於是內存計算,所以速度上會快很多,性能也更好。
這里還是應該選擇小表作為驅動表,因為如果驅動表太大,那么就需要將驅動表分段載入內存,將驅動表分成多少段,那么就需要掃描被驅動表多少次。所以這里還是推薦使用小表作為的驅動表。
結論:
如果可以使用被驅動表的索引,join語句非常有優勢
不能使用被驅動表的索引,只能使用Block Nested-Loop Join算法,盡量不要使用
在使用join的時候,應選擇小表作為驅動表