MySQL多表連接優化一例

1 set environment


DROP TABLE IF EXISTS a, b, c, d, e; 

CREATE TABLE a ( a_0 INT, b_0 INT, KEY (a_0), KEY (b_0) ); 
CREATE TABLE b ( b_0 INT, PRIMARY KEY (b_0) ); 
CREATE TABLE c ( c_0 INT, c_1 INT, c_2 INT, a_0 INT, KEY (c_0), KEY (c_1) ); 
CREATE TABLE d ( c_0 INT, e_0 INT, KEY (c_0) ); 
CREATE TABLE e ( e_0 INT, e_1 INT, PRIMARY KEY (e_0), KEY (e_1) ); 

EXPLAIN SELECT 1 FROM 

LEFT JOIN c AS c0 ON c0.a_0 = a.a_0 
LEFT JOIN d ON d.c_0 = c0.c_0 
LEFT JOIN c AS c1 ON c1.a_0 = a.a_0 
LEFT JOIN c AS c2 ON c2.a_0 = a.a_0 
LEFT JOIN c AS c3 ON c3.a_0 = a.a_0 
LEFT JOIN c AS c4 ON c4.a_0 = a.a_0 
LEFT JOIN c AS c5 ON c5.a_0 = a.a_0 
LEFT JOIN c AS c6 ON c6.a_0 = a.a_0 
LEFT JOIN c AS c7 ON c7.a_0 = a.a_0 
LEFT JOIN c AS c8 ON c8.a_0 = a.a_0 
LEFT JOIN c AS c9 ON c9.a_0 = a.a_0 
JOIN b AS b1 ON b1.b_0 = a.b_0 
JOIN b AS b2 ON b2.b_0 = a.b_0 
JOIN b AS b3 ON b3.b_0 = a.b_0 
JOIN b AS b4 ON b4.b_0 = a.b_0 
JOIN b AS b5 ON b5.b_0 = a.b_0 
JOIN b AS b6 ON b6.b_0 = a.b_0 
JOIN b AS b7 ON b7.b_0 = a.b_0 
JOIN b AS b8 ON b8.b_0 = a.b_0 
JOIN b AS b9 ON b9.b_0 = a.b_0 
LEFT JOIN c as c10 ON c10.a_0 = a.a_0 
AND c10.c_1 IS NULL 
LEFT JOIN e AS e1 ON e1.e_0 = c10.c_2 
JOIN b AS b10 ON b10.b_0 = a.b_0 
LEFT JOIN e AS e2 ON e2.e_0 = d.e_0 
AND e2.e_1 IS NULL;


2 execute "EXPLAIN SELECT 1 FROM...", it run very slowly. Why?

mysql> select version();

+--------------+

| version() |

+--------------+

| 6.1.10 |

+--------------+

1 row in set (0.00 sec)



mysql> EXPLAIN SELECT 1 FROM

-> a

-> LEFT JOIN c AS c0 ON c0.a_0 = a.a_0

-> LEFT JOIN d ON d.c_0 = c0.c_0

-> LEFT JOIN c AS c1 ON c1.a_0 = a.a_0

-> LEFT JOIN c AS c2 ON c2.a_0 = a.a_0

-> LEFT JOIN c AS c3 ON c3.a_0 = a.a_0

-> LEFT JOIN c AS c4 ON c4.a_0 = a.a_0

-> LEFT JOIN c AS c5 ON c5.a_0 = a.a_0

-> LEFT JOIN c AS c6 ON c6.a_0 = a.a_0

-> LEFT JOIN c AS c7 ON c7.a_0 = a.a_0

-> LEFT JOIN c AS c8 ON c8.a_0 = a.a_0

-> LEFT JOIN c AS c9 ON c9.a_0 = a.a_0

-> JOIN b AS b1 ON b1.b_0 = a.b_0

-> JOIN b AS b2 ON b2.b_0 = a.b_0

-> JOIN b AS b3 ON b3.b_0 = a.b_0

-> JOIN b AS b4 ON b4.b_0 = a.b_0

-> JOIN b AS b5 ON b5.b_0 = a.b_0

-> JOIN b AS b6 ON b6.b_0 = a.b_0

-> JOIN b AS b7 ON b7.b_0 = a.b_0

-> JOIN b AS b8 ON b8.b_0 = a.b_0

-> JOIN b AS b9 ON b9.b_0 = a.b_0

-> LEFT JOIN c as c10 ON c10.a_0 = a.a_0

-> AND c10.c_1 IS NULL

-> LEFT JOIN e AS e1 ON e1.e_0 = c10.c_2

-> JOIN b AS b10 ON b10.b_0 = a.b_0

-> LEFT JOIN e AS e2 ON e2.e_0 = d.e_0

-> AND e2.e_1 IS NULL;

+----+-------------+-------+--------+---------------+---------+---------+--------------+------+--------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+-------+--------+---------------+---------+---------+--------------+------+--------------------------+

| 1 | SIMPLE | a | ALL | b_0 | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c10 | ref | c_1 | c_1 | 5 | const | 0 | |

| 1 | SIMPLE | b1 | eq_ref | PRIMARY | PRIMARY | 4 | test.a.b_0 | 1 | Using index |

| 1 | SIMPLE | b2 | eq_ref | PRIMARY | PRIMARY | 4 | test.a.b_0 | 1 | Using index |

| 1 | SIMPLE | b3 | eq_ref | PRIMARY | PRIMARY | 4 | test.a.b_0 | 1 | Using index |

| 1 | SIMPLE | b4 | eq_ref | PRIMARY | PRIMARY | 4 | test.b3.b_0 | 1 | Using where; Using index |

| 1 | SIMPLE | b5 | eq_ref | PRIMARY | PRIMARY | 4 | test.a.b_0 | 1 | Using index |

| 1 | SIMPLE | b6 | eq_ref | PRIMARY | PRIMARY | 4 | test.b1.b_0 | 1 | Using where; Using index |

| 1 | SIMPLE | b7 | eq_ref | PRIMARY | PRIMARY | 4 | test.a.b_0 | 1 | Using index |

| 1 | SIMPLE | b8 | eq_ref | PRIMARY | PRIMARY | 4 | test.b7.b_0 | 1 | Using where; Using index |

| 1 | SIMPLE | b9 | eq_ref | PRIMARY | PRIMARY | 4 | test.b8.b_0 | 1 | Using where; Using index |

| 1 | SIMPLE | c0 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | d | ref | c_0 | c_0 | 5 | test.c0.c_0 | 1 | |

| 1 | SIMPLE | e2 | ref | PRIMARY,e_1 | e_1 | 5 | const | 0 | Using index |

| 1 | SIMPLE | e1 | eq_ref | PRIMARY | PRIMARY | 4 | test.c10.c_2 | 1 | Using index |

| 1 | SIMPLE | b10 | eq_ref | PRIMARY | PRIMARY | 4 | test.b9.b_0 | 1 | Using where; Using index |

| 1 | SIMPLE | c1 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c2 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c3 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c4 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c5 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c6 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c7 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c8 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c9 | ALL | NULL | NULL | NULL | NULL | 1 | |

+----+-------------+-------+--------+---------------+---------+---------+--------------+------+--------------------------+

25 rows in set (52.83 sec)


3 analyse and solve it

mysql> show variables like 'optimizer_search_depth';

+------------------------+-------+

| Variable_name | Value |

+------------------------+-------+

| optimizer_search_depth | 62 |

+------------------------+-------+

1 row in set (0.00 sec)


If we set optimizer_search_depth, sql run quickly

mysql> set optimizer_search_depth = 7;

Query OK, 0 rows affected (0.00 sec)


mysql> EXPLAIN SELECT 2 FROM

-> a

-> LEFT JOIN c AS c0 ON c0.a_0 = a.a_0

-> LEFT JOIN d ON d.c_0 = c0.c_0

-> LEFT JOIN c AS c1 ON c1.a_0 = a.a_0

-> LEFT JOIN c AS c2 ON c2.a_0 = a.a_0

-> LEFT JOIN c AS c3 ON c3.a_0 = a.a_0

-> LEFT JOIN c AS c4 ON c4.a_0 = a.a_0

-> LEFT JOIN c AS c5 ON c5.a_0 = a.a_0

-> LEFT JOIN c AS c6 ON c6.a_0 = a.a_0

-> LEFT JOIN c AS c7 ON c7.a_0 = a.a_0

-> LEFT JOIN c AS c8 ON c8.a_0 = a.a_0

-> LEFT JOIN c AS c9 ON c9.a_0 = a.a_0

-> JOIN b AS b1 ON b1.b_0 = a.b_0

-> JOIN b AS b2 ON b2.b_0 = a.b_0

-> JOIN b AS b3 ON b3.b_0 = a.b_0

-> JOIN b AS b4 ON b4.b_0 = a.b_0

-> JOIN b AS b5 ON b5.b_0 = a.b_0

-> JOIN b AS b6 ON b6.b_0 = a.b_0

-> JOIN b AS b7 ON b7.b_0 = a.b_0

-> JOIN b AS b8 ON b8.b_0 = a.b_0

-> JOIN b AS b9 ON b9.b_0 = a.b_0

-> LEFT JOIN c as c10 ON c10.a_0 = a.a_0

-> AND c10.c_1 IS NULL

-> LEFT JOIN e AS e1 ON e1.e_0 = c10.c_2

-> JOIN b AS b10 ON b10.b_0 = a.b_0

-> LEFT JOIN e AS e2 ON e2.e_0 = d.e_0

-> AND e2.e_1 IS NULL;

+----+-------------+-------+--------+---------------+---------+---------+--------------+------+--------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+-------+--------+---------------+---------+---------+--------------+------+--------------------------+

| 1 | SIMPLE | a | ALL | b_0 | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c10 | ref | c_1 | c_1 | 5 | const | 0 | |

| 1 | SIMPLE | c0 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | d | ref | c_0 | c_0 | 5 | test.c0.c_0 | 1 | |

| 1 | SIMPLE | e2 | ref | PRIMARY,e_1 | e_1 | 5 | const | 0 | Using index |

| 1 | SIMPLE | c1 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c2 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c3 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | e1 | eq_ref | PRIMARY | PRIMARY | 4 | test.c10.c_2 | 1 | Using index |

| 1 | SIMPLE | b1 | eq_ref | PRIMARY | PRIMARY | 4 | test.a.b_0 | 1 | Using index |

| 1 | SIMPLE | b2 | eq_ref | PRIMARY | PRIMARY | 4 | test.a.b_0 | 1 | Using index |

| 1 | SIMPLE | b3 | eq_ref | PRIMARY | PRIMARY | 4 | test.a.b_0 | 1 | Using index |

| 1 | SIMPLE | b4 | eq_ref | PRIMARY | PRIMARY | 4 | test.b3.b_0 | 1 | Using where; Using index |

| 1 | SIMPLE | b5 | eq_ref | PRIMARY | PRIMARY | 4 | test.a.b_0 | 1 | Using index |

| 1 | SIMPLE | b6 | eq_ref | PRIMARY | PRIMARY | 4 | test.b1.b_0 | 1 | Using where; Using index |

| 1 | SIMPLE | b7 | eq_ref | PRIMARY | PRIMARY | 4 | test.a.b_0 | 1 | Using index |

| 1 | SIMPLE | b8 | eq_ref | PRIMARY | PRIMARY | 4 | test.b7.b_0 | 1 | Using where; Using index |

| 1 | SIMPLE | b9 | eq_ref | PRIMARY | PRIMARY | 4 | test.b8.b_0 | 1 | Using where; Using index |

| 1 | SIMPLE | b10 | eq_ref | PRIMARY | PRIMARY | 4 | test.b9.b_0 | 1 | Using where; Using index |

| 1 | SIMPLE | c4 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c5 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c6 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c7 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c8 | ALL | NULL | NULL | NULL | NULL | 1 | |

| 1 | SIMPLE | c9 | ALL | NULL | NULL | NULL | NULL | 1 | |

+----+-------------+-------+--------+---------------+---------+---------+--------------+------+--------------------------+

25 rows in set (0.01 sec)


結論:

MySQL 對於多表連接算法,通常採取的是貪婪算法(greedy_search函數),這個函數對於多表連接的空間進行限制,如果表很多,則連接的順序的各種可能解則會很多,如果搜索空間大,則搜索耗費的時間就會多(因爲每種檢查都會被確認一遍).所以查詢執行計劃生成就會很慢.系統參數'optimizer_search_depth'就是限制搜索空間大小的.所以設置爲一個較小的值,有利於很快得到查詢執行計劃但得到的很可能不是最優的.


啓示:

如果在MySQL V5.5.X上運行多個表的連接(外連接/內連接等都包括),如果查詢執行計劃獲得速度較慢(如超過一秒),則可以查看其'optimizer_search_depth'參數的設置,並修改它,使得生成查詢執行計劃的速度變快。


另外,對於參數'optimizer_search_depth',MySQL的默認值給的是62,這個是不合適的,請在多表連接時稍加註意,避免因這個參數的值影響到查詢執行計劃的生成速度.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章