1. 程式人生 > >MySQL調優三部曲(二)EXPLAIN

MySQL調優三部曲(二)EXPLAIN

MySQL調優三部曲(二)EXPLAIN


EXPLAIN
MySQL Query Optimizer通過執行EXPLAIN命令來告訴我們它將使用一個怎樣的執行計劃優化Query。所以,通過Explain可以幫助我們選擇更好的索引和寫出更優化的查詢語句

Explain各種資訊的解釋
PS:下面列舉的例子有些是無意義的,只是為了展示explain的效果

1. id
查詢序列號,id大的先執行,相同的id按從上往下順序依次執行,id列為NULL表示一個結果集,不是查詢

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: 結果集無法快取的子查詢

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:
查詢的表名

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是根據表定義計算而得

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各輸出資訊的解釋,那哪些引數是我們需要重點關注的呢?
一、select_type
當select_type是dependent union、dependent subquery,這種結果需要依賴外部查詢結果集的查詢,就需要進行優化了,一般的優化方案有:1、使用臨時表聯表查詢 2、分成兩個查詢順序執行

二、type
當type是index_merge,range,index,all時,可以進行選擇採用以下幾種優化方案:1、採用聯合索引 2、對全表掃描的可以採取新建索引

三、extra
當extra是using filesort,using temporary,using where,Using join buffer時,可以採取以下幾種優化方案:1、排序儘量用索引欄位 2、儘量用索引欄位進行多表連線 3、頻繁查詢的欄位建立索引