MySQL調優三部曲(二)EXPLAIN
EXPLAIN
MySQL Query Optimizer經過執行EXPLAIN命令來告訴咱們它將使用一個怎樣的執行計劃優化Query。因此,經過Explain能夠幫助咱們選擇更好的索引和寫出更優化的查詢語句mysql
Explain各類信息的解釋
PS:下面列舉的例子有些是無心義的,只是爲了展現explain的效果算法
1. id
查詢序列號,id大的先執行,相同的id按從上往下順序依次執行,id列爲NULL表示一個結果集,不是查詢sql
2. select_type(查詢中每一個select子句的類型)
simple: 除子查詢或UNION以外的其餘查詢
mysql> explain select * from focus;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | focus | NULL | ALL | NULL | NULL | NULL | NULL | 33 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
primary: UNION或者含有子查詢的select,位於最外層的查詢就是primary
mysql> explain select object_id from focus a where id = 1 union select object_id from focus b where id = 3;
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
| 1 | PRIMARY | a | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 2 | UNION | b | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)
union: UNION語句第二個select開始後面全部的select,第一個select是primary
mysql> explain select object_id from focus a where id = 1 union select object_id from focus b where id = 3;
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
| 1 | PRIMARY | a | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 2 | UNION | b | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)
dependent union: 和 union 相似,出如今UNION語句中,可是這個查詢依賴於外部查詢的結果集。在下面的sql語句中,依賴於外部查詢的結果集的意思是,MySQL會先執行select * from focus,獲得全部結果以後,再一條一條地去與子查詢SQL組成新的查詢語句,可想而知,這種查詢類型是很是慢的
mysql> explain select status from focus a where id in (select id from favour where object_id = 19931224 union select id from favour where object_id = 19931226);
+----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+-----------------+
| 1 | PRIMARY | a | NULL | ALL | NULL | NULL | NULL | NULL | 33 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | favour | NULL | eq_ref | PRIMARY | PRIMARY | 4 | func | 1 | 10.00 | Using where |
| 3 | DEPENDENT UNION | favour | NULL | eq_ref | PRIMARY | PRIMARY | 4 | func | 1 | 10.00 | Using where |
| NULL | UNION RESULT | <union2,3> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+-----------------+
4 rows in set, 1 warning (0.00 sec)
union result: union的結果,由於它不須要參與查詢,因此id字段爲null
mysql> explain select status from focus a union select status from favour where object_id = 19931224;
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | PRIMARY | a | NULL | ALL | NULL | NULL | NULL | NULL | 33 | 100.00 | NULL |
| 2 | UNION | favour | NULL | ALL | NULL | NULL | NULL | NULL | 31 | 10.00 | Using where |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
3 rows in set, 1 warning (0.01 sec)
subquery: 子查詢內層查詢的第一個select,結果不依賴於外部查詢結果集
mysql> explain select status from focus a where id = (select id from favour where object_id = 19931224);
+----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+--------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+--------------------------------+
| 1 | PRIMARY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | no matching row in const table |
| 2 | SUBQUERY | favour | NULL | index | NULL | infoByUser | 13 | NULL | 31 | 10.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+--------------------------------+
2 rows in set, 1 warning (0.01 sec)
dependent subquery: 子查詢內層查詢的第一個select,結果依賴於外部查詢結果集(好比SELECT XXX FROM TABLE1 WHERE status = 1 AND id IN (SELECT XXX FROM TABLE2 WHERE SID IN (1,3,5,7,9)),在這條語句中,table2的查詢就是dependent subquery,MySQL首先根據SELECT XXX FROM TABLE1 WHERE status = 1獲得一個大的結果集,再將大的結果集中的每一條記錄,都與子查詢SQL組成新的查詢語句,這就是結果依賴於外部查詢結果的意思,慢查優化-DEPENDENT SUBQUERY)緩存
uncacheable subquery: 結果集沒法緩存的子查詢ide
derived: 用於from子句裏有子查詢的狀況,MySQL會遞歸執行這些子查詢,把結果放在臨時表中(例:SELECT g1.gid,count(1) FROM shop_goods g1, (select gid from shop_goods WHERE sid in (1519066,1453929)) g2 where g1.status=0 and g1.gid=g2.gid GROUP BY g1.gid;)函數
3. table:
查詢的表名oop
4. type (表示MySQL在表中找到所需行的方式,又稱「訪問類型」,從最佳類型到最差類型依次列舉)
const: 用到 primary key 或者unique 索引(表最多隻有一個匹配行),const是最優化的
mysql> explain select * from focus where id = 1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | focus | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
eq_ref: 惟一索引或者主鍵索引做爲兩個表的聯接方式,可使用=比較兩個索引列
mysql> explain select a.id from focus a,favour b where a.id = b.id;
+----+-------------+-------+------------+--------+---------------+------------+---------+------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+------------+---------+------------+------+----------+-------------+
| 1 | SIMPLE | b | NULL | index | PRIMARY | infoByUser | 13 | NULL | 31 | 100.00 | Using index |
| 1 | SIMPLE | a | NULL | eq_ref | PRIMARY | PRIMARY | 4 | drama.b.id | 1 | 100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+------------+---------+------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
ref: 使用索引,但不是惟一索引或者主鍵索引
mysql> explain select * from video where up_id = 123;
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+-------+
| 1 | SIMPLE | video | NULL | ref | up_id | up_id | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
ref_or_null: 相似於ref,還能夠額外查詢含有NULL值得行,多用在子查詢中
上面5種是比較合理的索引使用狀況
index_merge: 使用多個索引查找後的交集/並集定位數據
mysql> explain select * from video where up_id = 123 or id = 3;
+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+-----------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+-----------------------------------------+
| 1 | SIMPLE | video | NULL | index_merge | PRIMARY,up_id | up_id,PRIMARY | 4,4 | NULL | 2 | 100.00 | Using union(up_id,PRIMARY); Using where |
+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+-----------------------------------------+
1 row in set, 1 warning (0.00 sec)
range: 對索引列進行範圍查找,如in操做,between操做,>、<、=操做等
mysql> explain select * from video where id > 123;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | video | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 1 | 100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
index: 是全表掃描,可是隻select索引列的值,因此只須要掃描索引樹便可
mysql> explain select id from video;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-------------+
| 1 | SIMPLE | video | NULL | index | NULL | up_id | 4 | NULL | 24 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
all: 掃全表,而後再在server層進行過濾返回符合要求的記錄
5. possible_keys
指出MySQL能使用哪一個索引在表中找到記錄,查詢涉及到的字段上若存在索引,則該索引將被列出,但不必定被查詢使用性能
6. keys
當前query實際使用的索引優化
7. key_len
表示使用的索引的長度,key_len顯示的值爲索引字段的最大可能長度,並不是實際使用長度,即key_len是根據表定義計算而得server
8. ref
表示上述表的鏈接匹配條件,即哪些列或常量被用於查找索引列上的值
mysql> explain select * from focus a , favour b where a.id = b.id ;
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+------+----------+-------+
| 1 | SIMPLE | a | NULL | ALL | PRIMARY | NULL | NULL | NULL | 35 | 100.00 | NULL |
| 1 | SIMPLE | b | NULL | eq_ref | PRIMARY | PRIMARY | 4 | drama.a.id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
9. rows
執行MySQL查詢的行數
10. extra
distinct: select使用distinct關鍵字時出現(實際使用distinct操做沒做用)
using index: 當前的SELECT操做使用了覆蓋索引,query能夠直接利用索引返回SELECT的字段,而沒必要根據索引再去讀取數據文件(覆蓋索引:包含全部知足查詢須要的數據的索引)
mysql> explain select id from focus;
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
| 1 | SIMPLE | focus | NULL | index | NULL | object | 9 | NULL | 35 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
using filesort: 沒法利用索引完成的排序
mysql> explain select * from favour order by status;
+----+-------------+--------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+----------------+
| 1 | SIMPLE | favour | ALL | NULL | NULL | NULL | NULL | 7 | Using filesort |
+----+-------------+--------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)
using temporary: 爲了解決查詢,MySQL須要建立一個臨時表來容納結果,常見於排序和分組查詢,出現這種狀況須要優化。以下查詢,兩條sql語句的區別只在於order by的字段不一樣,可是一條用到了臨時表,一條沒用到。MySQL的表關聯算法是Nest Loop Join,經過驅動表的結果集做爲循環基礎數據,而後將該結果集中的數據做爲過濾條件到下一個表中去查詢數據。經過EXPLAIN的結果,第一行出現的表就是驅動表,驅動表能夠直接排序,而非驅動表須要用臨時表存儲合併結果,而後再進行排序。
mysql> explain select a.status from favour a, favour_1 b where a.id = b.status order by a.id;
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+---------------------------------+
| 1 | SIMPLE | b | ALL | NULL | NULL | NULL | NULL | 1 | Using temporary; Using filesort |
| 1 | SIMPLE | a | eq_ref | PRIMARY | PRIMARY | 4 | test.b.status | 1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+---------------------------------+
2 rows in set (0.00 sec)
mysql> explain select a.status from favour a, favour_1 b where a.id = b.status order by b.id;
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+-------------+
| 1 | SIMPLE | b | index | NULL | PRIMARY | 4 | NULL | 1 | NULL |
| 1 | SIMPLE | a | eq_ref | PRIMARY | PRIMARY | 4 | test.b.status | 1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+-------------+
2 rows in set (0.00 sec)
using where: 使用where子句匹配數據,且where條件列非索引列
mysql> explain select * from favour where status = 1;
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | favour | ALL | NULL | NULL | NULL | NULL | 7 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
Using sort_union(...), Using union(...),Using intersect(...): 這些函數說明如何爲index_merge聯接類型合併索引掃描,如將兩個索引結果並集,或者兩個索引結果交集
Using index for group-by: 相似於訪問表的Using index方式,區別是Using index採用的是緊湊索引,Using index for group-by採用的是鬆散索引,Using index for group-by表示MySQL發現了一個索引,能夠用來查詢GROUP BY或DISTINCT查詢的全部列,而不要額外搜索硬盤訪問實際的表,既group by字段就是索引列。而且,按最有效的方式使用索引,以便對於每一個組,只讀取少許索引條目
Using join buffer:
該值強調了在獲取鏈接條件時沒有使用索引,而且須要鏈接緩衝區來存儲中間結果。若是出現了這個值,那應該注意,根據查詢的具體狀況可能須要添加索引來改進性能
mysql> explain select * from favour a join favour_1 b on a.status = b.status;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
| 1 | SIMPLE | b | ALL | NULL | NULL | NULL | NULL | 1 | NULL |
| 1 | SIMPLE | a | ALL | NULL | NULL | NULL | NULL | 7 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
2 rows in set (0.00 sec)
Impossible where: 這個值強調了where語句會致使沒有符合條件的行
mysql> explain select * from favour where 1 = 2;
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
1 row in set (0.00 sec)
Select tables optimized away: 該值意味着僅經過使用索引,優化器可能僅從聚合函數結果中返回一行
mysql> explain select max(id) from favour;
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
1 row in set (0.00 sec)
優化總結
上文詳細解釋了explain各輸出信息的解釋,那哪些參數是咱們須要重點關注的呢?
1、select_type
當select_type是dependent union、dependent subquery,這種結果須要依賴外部查詢結果集的查詢,就須要進行優化了,通常的優化方案有:一、使用臨時表聯表查詢 二、分紅兩個查詢順序執行
2、type
當type是index_merge,range,index,all時,能夠進行選擇採用如下幾種優化方案:一、採用聯合索引 二、對全表掃描的能夠採起新建索引
3、extra當extra是using filesort,using temporary,using where,Using join buffer時,能夠採起如下幾種優化方案:一、排序儘可能用索引字段 二、儘可能用索引字段進行多表鏈接 三、頻繁查詢的字段創建索引