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

OldBoy~發表於2018-01-19

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

準備工作

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。下面就來拿幾張表和語句全面說明一下。

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表。

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操作

相關文章