Mysql性能調優工具Explain結合語句講解

Explain簡稱執行計劃,能夠模擬SQL語句,來分析查詢語句或者表結構是否有性能瓶頸。
Explain的做用有哪些,能夠看到哪些?
能夠看到表的讀取順序,數據讀取操做的操做類型,哪些索引可使用,哪些索引被實際應用,表之間的引用,每張表有多少行被優化器查詢。mysql

準備工做

DROP TABLE IF EXISTS `t1`;
CREATE TABLE `t1` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `other_column` varchar(30) NOT NULL DEFAULT '',
  `other_column2` varchar(30) NOT NULL DEFAULT '',
  `other_column3` varchar(30) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `idx` (`other_column`),
  KEY `u_idx` (`other_column2`,`other_column3`),
  KEY `u_idx2` (`other_column`,`other_column2`,`other_column3`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t1
-- ----------------------------
INSERT INTO `t1` VALUES ('1', 'A', 'D', 'L');
INSERT INTO `t1` VALUES ('2', 'B', 'E', 'M');
INSERT INTO `t1` VALUES ('3', 'C', '', 'N');
INSERT INTO `t1` VALUES ('4', '', 'F', '');
INSERT INTO `t1` VALUES ('5', 'F', 'G', 'O');
INSERT INTO `t1` VALUES ('6', 'A', 'H', 'P');

-- ----------------------------
-- Table structure for t2
-- ----------------------------
DROP TABLE IF EXISTS `t2`;
CREATE TABLE `t2` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `other_column` varchar(30) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `idx` (`other_column`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t2
-- ----------------------------
INSERT INTO `t2` VALUES ('1', 'C');
INSERT INTO `t2` VALUES ('2', 'D');
INSERT INTO `t2` VALUES ('3', 'E');
INSERT INTO `t2` VALUES ('4', '');
INSERT INTO `t2` VALUES ('5', 'G');

-- ----------------------------
-- Table structure for t3
-- ----------------------------
DROP TABLE IF EXISTS `t3`;
CREATE TABLE `t3` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `other_column` varchar(30) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t3
-- ----------------------------
INSERT INTO `t3` VALUES ('1', 'F');
INSERT INTO `t3` VALUES ('2', 'G');
INSERT INTO `t3` VALUES ('3', 'H');
INSERT INTO `t3` VALUES ('4', '');
INSERT INTO `t3` VALUES ('5', 'I');

在使用Explain分析SQL語句以後,會出現這些列,分別是id、type、tabl、select_type、possible_keys、key、key_len、ref、rows、Extra。下面就來拿幾張表和語句全面說明一下。sql

id :select查詢的一個序列號,包含一組數字,表示查詢中執行select子句或者操做表的順序(有三種狀況)。
①id相同表示mysql內部的查詢優化器執行命令,也就是加載表的順序的,從上到下,也就是前後加載了t1,t2,t3,固然也有可能順序不是這樣。數據庫

EXPLAIN SELECT t2.* FROM t1,t2,t3 WHERE t1.id = t2.id AND t1.id = t3.id AND t1.other_column = '';
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref           | rows | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+-------------+
| 1  | SIMPLE      | t1    | ALL    | PRIMARY       | NULL    | NULL    | NULL          | 5    | Using where |
| 1  | SIMPLE      | t2    | eq_ref | PRIMARY       | PRIMARY | 4       | test_db.t1.id | 1    |             |
| 1  | SIMPLE      | t3    | eq_ref | PRIMARY       | PRIMARY | 4       | test_db.t1.id | 1    | Using index |
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+-------------+

②id不一樣性能

EXPLAIN SELECT t2.* FROM t2 WHERE id = (SELECT id FROM t1 WHERE id = (SELECT t3.id FROM t3 WHERE t3.other_column = ''));
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
| 1  | PRIMARY     | t2    | const | PRIMARY       | PRIMARY | 4       | const | 1    |             |
| 2  | SUBQUERY    | t1    | const | PRIMARY       | PRIMARY | 4       |       | 1    | Using index |
| 3  | SUBQUERY    | t3    | ALL   | NULL          | NULL    | NULL    | NULL  | 5    | Using where |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+

若是是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行。也就是先執行子查詢查t3的表語句,再t1。優化

③id相同存在,不一樣也存在。指針

EXPLAIN SELECT t2.* FROM(SELECT t3.id FROM t3 WHERE t3.other_column = '') s1,t2 WHERE s1.id = t2.id;
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------+
| 1  | PRIMARY     | <derived2> | system | NULL          | NULL    | NULL    | NULL  | 1    |             |
| 1  | PRIMARY     | t2         | const  | PRIMARY       | PRIMARY | 4       | const | 1    |             |
| 2  | DERIVED     | t3         | ALL    | NULL          | NULL    | NULL    | NULL  | 5    | Using where |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------+

分析結果可看出,先走id最大的2,也就是先走括號裏面的查t3表的語句。走完查t3後,順序執行,有一個<derived2>,derived是衍生的意思,意思是在執行完t3查詢後的s1虛表基礎上,<derived2>中的2,就是id爲2的。最後執行的查t2表。code

select_type(數據讀取操做的操做類型)排序

常見經常使用的6個值分別是:SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION、UNION RESULT,主要是告訴開發者查詢的類型,爲了區別是普通查詢、聯合查詢、子查詢等複雜的查詢。
SIMPLE:最簡單的查詢,查詢中不包含子查詢或者UNION。
PRIMARY:查詢中若包含任何複雜的子部分,最外層查詢則被標記爲PRIMARY,也就是最後加載的那個。(如上面查詢分析語句)
SUBQUERY:在SELECT或者WHERE列表中包含了子查詢
DERIVED:在FROM列表中包含的子查詢被標記爲DERIVED(衍生)Mysql會遞歸執行這些子查詢,把結果放在臨時表裏(如上面查詢分析語句)
UNION:若第二個SELECT出如今UNION以後,則被標記爲UNION;若UNION包含在FROM子句的子查詢中,外層SELECT將被標記爲DERIVED
UNION RESULT:兩種UNION語句的合併。遞歸

table(表示查詢涉及的表或衍生表)索引

type

反應的結果和mysql是否優化過,是不是最佳狀態息息相關。
常見的大概7種:
從最好到最差的結果依次以下:
system > const > eq_ref > ref > range > index > ALL

system:表只有一行記錄(等於系統表),這是const類型的特例,平時不會出現,這個也能夠忽略不計,且只能用於myisam和memory表。若是是Innodb引擎表,type列在這個狀況一般都是all或者index。
const:表示經過索引一次就找到了,const用於比較primary key或者unique索引。由於只匹配一行數據,因此很快如將主題置於WHERE列表中,Mtsql就能將該查詢轉換爲一個常量。以下

explain select * from (select * from t1 where id =1) d1;
+----+-------------+------------+--------+---------------+---------+---------+------+------+-------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref  | rows | Extra |
+----+-------------+------------+--------+---------------+---------+---------+------+------+-------+
|  1 | PRIMARY     | <derived2> | system | NULL          | NULL    | NULL    | NULL |    1 |       |
|  2 | DERIVED     | t1         | const  | PRIMARY       | PRIMARY | 4       |      |    1 |       |
+----+-------------+------------+--------+---------------+---------+---------+------+------+-------+

eq_ref :惟一性索引掃描,對於每一個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或者惟一索引掃描

explain select * from t1,t2 where t1.id = t2.id;
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+-------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref           | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+-------+
|  1 | SIMPLE      | t1    | ALL    | PRIMARY       | NULL    | NULL    | NULL          |    5 |       |
|  1 | SIMPLE      | t2    | eq_ref | PRIMARY       | PRIMARY | 4       | test_db.t1.id |    1 |       |
+----+-------------+-------+--------+---------------+---------+---------+---------------+------+-------+

ref:非惟一性索引或非主鍵索引掃描,或者是使用了 最左前綴 規則索引的查詢,返回匹配某個單獨值得全部行。本質上也是一種索引訪問,它返回全部匹配某個單獨值得行,然而,它可能會找到多個符合條件的行,因此他應該屬於朝趙和掃描的混合體。

create index idx on t1(other_column);
explain select * from t1 where other_column ='A';
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
|  1 | SIMPLE      | t1    | ref  | idx           | idx  | 92      | const |    1 | Using where |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+

range:只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了哪一個索引。通常就是在你的WHERE語句中出現了BETWEEN、<、>、IN等的查詢,這種範圍掃描索引比全表掃描更好,由於它只須要開始於索引的某一點,而結束語另外一點不用掃描所有索引。

explain select * from t1 where id between 2 and 5;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | range | PRIMARY       | PRIMARY | 4       | NULL |    3 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+

index: 表示全索引掃描(full index scan), 和 ALL 類型相似, 只不過 ALL 類型是全表掃描, 而 index 類型則僅僅掃描全部的索引, 而不掃描數據,比ALL稍微好點,若是表數據不小,必須優化。
index 類型一般出如今: 所要查詢的數據直接在索引樹中就能夠獲取到, 而不須要掃描數據. 當是這種狀況時, Extra 字段 會顯示 Using index.

explain select id from t1;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | index | NULL          | PRIMARY | 4       | NULL |    6 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+

ALL: 表示全表掃描, 這個類型的查詢是性能最差的查詢之一. 一般來講, 咱們的查詢不該該出現 ALL 類型的查詢, 由於這樣的查詢在數據量大的狀況下, 對數據庫的性能是巨大的災難. 如一個查詢是 ALL 類型查詢, 那麼通常來講能夠對相應的字段添加索引來避免.
下面是一個全表掃描的例子, 能夠看到, 在全表掃描時, possible_keys 和 key 字段都是 NULL, 表示沒有使用到索引, 而且 rows 十分巨大, 所以整個查詢效率是十分低下的.

explain select * from t2 where other_column = '';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t2    | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

possible_keys 和 key

顯示可能應用在這張表中的索引,一個或者多個。查詢涉及到的字段上若存在索引,則該索引將被列出,但不必定被查詢實際使用。也就是說possible_keys是推測可能用到哪些索引,而key是實際用到的。這裏例子不少,不舉例

key_len

表示索引中使用的字節數,可經過該列計算查詢中使用的索引的長度。在不損失精確性的狀況下,長度越短越好。key_len顯示的值爲索引字段的最大可能長度,並不是實際使用長度。即key_len是根據表定義計算而得,不是經過表內檢索出的

ref

顯示索引的哪一段被使用了,若是可能的話,是一個常數。哪些列或常量被用於查找索引列上的值。

rows

根據表統計信息及搜索選用狀況,大體估算出找到所需的記錄所須要讀取的行數,固然越小越好

extra(列返回的描述的意義,這裏只說常見的)

Using filesort:看到這個的時候,查詢就須要優化了。說明Mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。Mysql中沒法利用索引完成的排序操做稱之爲文件排序。Mysql須要進行額外的步驟來發現如何對返回的行排序。它根據鏈接類型以及存儲排序鍵值和匹配條件的所有行的行指針來排序所有行。

explain select other_column from t1 where other_column = 'A' order by other_column3;
+----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra                       |
+----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------------+
|  1 | SIMPLE      | t1    | ref  | idx           | idx  | 92      | const |    1 | Using where; Using filesort |
+----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------------+

優化案例:

create index u_idx2 on t1(other_column,other_column2,other_column3);
EXPLAIN SELECT other_column FROM t1 WHERE other_column = 'A'ORDER BY other_column2,other_column3;
+----+-------------+-------+------+---------------+--------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key    | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+--------+---------+-------+------+-------------+
|  1 | SIMPLE      | t1    | ref  | idx,u_idx2    | u_idx2 | 92      | const |    1 | Using where |
+----+-------------+-------+------+---------------+--------+---------+-------+------+-------------+

Using index: 列數據是從僅僅使用了索引中的信息而沒有讀取實際的行動的表返回的,這發生在對錶的所有的請求列都是同一個索引的部分的時候。表示相應的select操做中使用了覆蓋索引(Coverindex ing),避免訪問了表的數據航,效果理想!若是同時出現using where,表示索引被用來執行索引鍵值的查找;若是沒有同時出現using where,表示索引用來讀取數據而非執行查找動做。

覆蓋索引的含義: 就是select的數據列只用從索引中就可以取得,沒必要讀取數據行,Mysql能夠利用索引返回select列表中的字段,而沒必要根據索引再次讀取數據文件,換句話說查詢列要被所建的索引覆蓋。注意的是,若是要使用覆蓋索引,必定要注意select列表中只讀取出須要的列,而不是select *,由於若是將全部字段一塊兒作索引會致使索引文件過大,下降查詢性能。

EXPLAIN SELECT other_column FROM t1 WHERE other_column in ('A','B','C') group by other_column,other_column2;
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+
|  1 | SIMPLE      | t1    | range | idx,u_idx2    | u_idx2 | 92      | NULL |    3 | Using where; Using index |
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+
EXPLAIN SELECT other_column,other_column2,other_column3 FROM t1;
+----+-------------+-------+-------+---------------+--------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+--------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | index | NULL          | u_idx2 | 276     | NULL |    6 | Using index |
+----+-------------+-------+-------+---------------+--------+---------+------+------+-------------+

Using temporary:看到這個的時候,查詢須要優化了。這裏,Mysql須要建立一個臨時表來存儲結果,這一般發生在對不一樣的列集進行ORDER BY上和GROUP BY上,拖慢與sql查詢。

EXPLAIN SELECT other_column FROM t1 WHERE other_column in ('A','B','C') group by other_column3;
+----+-------------+-------+-------+---------------+--------+---------+------+------+-----------------------------------------------------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows | Extra                                                     |
+----+-------------+-------+-------+---------------+--------+---------+------+------+-----------------------------------------------------------+
|  1 | SIMPLE      | t1    | range | idx,u_idx2    | u_idx2 | 92      | NULL |    3 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+-------+-------+---------------+--------+---------+------+------+-----------------------------------------------------------+

優化案例:

EXPLAIN SELECT other_column FROM t1 WHERE other_column in ('A','B','C') group by other_column,other_column2;
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+
|  1 | SIMPLE      | t1    | range | idx,u_idx2    | u_idx2 | 92      | NULL |    3 | Using where; Using index |
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+

Impossible WHERE:查詢語句老是false,不能查詢出任何數據,至關於要求一我的既是男性又是女性...

explain select * from t1 where other_column = 'A' and other_column= 'B';
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra            |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+

延伸案例(執行順序分析)

explain select d1.other_column,(select id from t3) d2 from (select id,other_column from t1 where other_column='') d1 union (select other_column,id from t2);
+------+--------------+------------+--------+---------------+---------+---------+------+------+-------------+
| id   | select_type  | table      | type   | possible_keys | key     | key_len | ref  | rows | Extra       |
+------+--------------+------------+--------+---------------+---------+---------+------+------+-------------+
|  1   | PRIMARY      | <derived3> | system | NULL          | NULL    | NULL    | NULL |    1 |             |
|  3   | DERIVED      | t1         | ref    | idx,u_idx2    | idx     | 92      |      |    1 | Using where |
|  2   | SUBQUERY     | t3         | index  | NULL          | PRIMARY | 4       | NULL |    5 | Using index |
|  4   | UNION        | t2         | ALL    | NULL          | NULL    | NULL    | NULL |    5 |             |
| NULL | UNION RESULT | <union1,4> | ALL    | NULL          | NULL    | NULL    | NULL | NULL |             |
+------+--------------+------------+--------+---------------+---------+---------+------+------+-------------+

第一行(執行順序4):id列爲1,表示union裏的第一個select,select_type列的primary表示該查詢爲外層查詢,table列被標記爲<derived3>,表示查詢結果來自一個衍生表,其中derived3中的3表示該查詢衍生自第三個select查詢,即id爲3的select。select d1.other_column....第二行(執行順序2):id列爲3,是整個查詢中的第三個select的一部分。因查詢包含在from中,因此derived。select id,other_column from t1 where other_column=''第三行(執行順序3):selct列表中的子查詢select_type爲subquery,爲整個查詢中的第二個select。select id from t3第四行(執行順序1):select_type爲union,說明第四個select是union裏的第二個select,最早執行。select other_column,id from t2第五行(執行順序5):表明從union的臨時表中讀取行的階段,table列的<union1,4>表示用第一個和第四個select的結果進行union操做。兩個結果union操做

相關文章
相關標籤/搜索