用了那麼多年MySQL不知道Explain?怪不得性能那麼差!

一 簡介

性能優化是一個開發或者dba不可少的工做內容,工欲善其事必先利其器,本文介紹一個輔助咱們查看sql執行計劃是否優化的工具,經過explain的結果,咱們能夠肯定sql是否利用正確的索引。sql

二 介紹

2.1 測試環境

MySQL 5.7數據庫

create table a(  id   bigint(20)  NOT NULL AUTO_INCREMENT,  name varchar(50) NOT NULL DEFAULT '',  age  INT(11) DEFAULT 0,  primary key (id),  key idx_name (name)) engine = innodb default charset= utf8;insert into a (name, age) values('yy', 11);insert into a (name, age) values('xx', 25);insert into a (name, age) values('yz', 23);insert into a (name, age) values('zhangcan', 32);insert into a (name, age) values('lisi', 18);insert into a (name, age) values('boshi', 62);insert into a (name, age) values('taisen', 52);insert into a (name, age) values('liuxiang', 32);insert into a (name, age) values('malong', 23);insert into a (name, age) values('jingtian', 28);create table b(  id   bigint(20)  NOT NULL AUTO_INCREMENT,  sid  int  not null default 0,  name varchar(50) NOT NULL DEFAULT '',  score  INT(11) DEFAULT 0,  primary key (id),  key idx_sid(sid),  key idx_name (name)) engine = innodb default charset= utf8;insert into b (sid,name, score) values(1,'yy', 99);insert into b (sid,name, score) values(1,'yy', 99);insert into b (sid,name, score) values(1,'yy', 99);insert into b (sid,name, score) values(2,'xx', 95);insert into b (sid,name, score) values(2,'xx', 95);insert into b (sid,name, score) values(3,'yz', 93);insert into b (sid,name, score) values(3,'yz', 93);insert into b (sid,name, score) values(4,'zhangcan', 90);insert into b (sid,name, score) values(5,'lisi', 88);insert into b (sid,name, score) values(5,'lisi', 80);insert into b (sid,name, score) values(5,'lisi', 78);insert into b (sid,name, score) values(6,'boshi', 83);insert into b (sid,name, score) values(6,'boshi', 80);insert into b (sid,name, score) values(6,'boshi', 92);insert into b (sid,name, score) values(7,'taisen', 85);insert into b (sid,name, score) values(8,'liuxiang', 81);insert into b (sid,name, score) values(9,'malong', 92);insert into b (sid,name, score) values(10,'jingtian', 78);insert into b (sid,name, score) values(10,'jingtian', 90);insert into b (sid,name, score) values(10,'jingtian', 88);insert into b (sid,name, score) values(10,'jingtian', 93);複製代碼

2.2 結果介紹

執行 explian 的結果以下:性能優化

test >explain select * from  a where id=3 \G*************************** 1. row ***************************           id: 1  select_type: SIMPLE        table: a   partitions: NULL         type: constpossible_keys: PRIMARY          key: PRIMARY      key_len: 8          ref: const         rows: 1     filtered: 100.00        Extra: NULL複製代碼

從上面的輸出咱們能夠看到,分別是id、type、tabl、selecttype、possiblekeys、key、key_len、ref、rows、Extra。本文主要以select語句爲例講解 explian的輸出。bash

三 解讀

3.1 id

查詢語句的序號或者說是標識符,每一個查詢語句包括子查詢都會分配一個id,表示查詢中執行select子句或者操做的順序,可能有以下幾種狀況併發

1 id值相同高併發

id 值相同通常出如今多表關聯的場景,訪問表的順序是從上到下 。工具

兩個id 都爲1,先訪問b表而後訪問a表。性能

2 id值不一樣測試

id 值不一樣的狀況,從大到小執行,值越大越先開始執行或者被訪問。優化

從結果來看,id爲2 那一行的子查詢先被執行。而後再去訪問id=1 中a表。

思考題 若是 a.id in (select sid from b where id=10); explai的結果會是什麼樣呢?

3 id 包含了相同和不一樣的狀況。

該狀況通常是現有2個表或者子查詢和表join ,而後在和第三個表關聯查詢。好比

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後,順序執行,有一個,derived是衍生的意思,意思是在執行完t3查詢後的s1虛表基礎上,中的2,就是id爲2的。最後執行的查t2表。

5.7的優化器針對子查詢作了不少優化,我本身沒有模擬出來場景3,故使用網上的例子。

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

常見的有以下6種:SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION、UNION RESULT,主要是告訴咱們查詢的類型是普通查詢、聯合查詢、子查詢等複雜的查詢。

SIMPLE:最簡單的查詢,查詢中不包含子查詢或者UNION。

PRIMARY:查詢中若包含任何複雜的子查詢,最外層查詢則被標記爲PRIMARY,也就是最後被執行的語句。

SUBQUERY:在SELECT from 或者WHERE列表中包含了子查詢

DERIVED:在FROM列表中包含的子查詢被標記爲DERIVED(衍生)MySQL會遞歸執行這些子查詢,把結果放在臨時表裏。

UNION:若第二個SELECT出如今UNION以後,則被標記爲UNION;若UNION包含在FROM子句的子查詢中,外層SELECT將被標記爲DERIVED

UNION RESULT:兩種UNION語句的合併。

DEPENDENT SUBQUERY: 子查詢中的第一個 SELECT, 取決於外面的查詢. 即子查詢依賴於外層查詢的結果. 出現該值的時候必定要特別注意,可能須要使用join的方式優化子查詢。

3.3 table(查詢涉及的表或衍生表)

其值爲表名或者表的別名,表示訪問哪個表,

當from中有子查詢的時候,表名是derivedN的形式,其中 N 指向子查詢,也就是explain結果中的下一列

當有union result的時候,表名是union 1,2等的形式,1,2表示參與union的query id

注意 MySQL對待這些表和普通表同樣,可是這些臨時表是沒有任何索引的。數據量大的狀況下可能會有性能問題。

3.4 type (表示訪問表的方式)

從最好到最差的結果依次以下:

system > const > eq_ref > ref > range > index > ALL

system: 表示結果集僅有一行。這是const聯接類型的一個特例,表須是myisam或者memory存儲引擎。若是是innodb存儲引擎,type 顯示爲 const 。

const: 表示經過主鍵或者惟一鍵鍵查找數據時只匹配最多一行數據。

eq_ref: 該類型多出如今多表join場景,經過主鍵或者惟一鍵訪問表.

對於前表b的每行記錄, 都只能匹配到後表a的一行記錄而且查詢的比較操做一般是 =,查詢效率較高.

ref: 此類型一般出如今sql使用非惟一或非主鍵索引, 或者是使用最左前綴規則索引的查詢. 例以下面這個例子中, 就使用到了 ref 類型的查詢:

range: 表示where條件使用索引範圍查詢, 經過索引字段範圍獲取表中部分數據記錄. 這個類型一般出如今 <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN() 操做中.當 type 是 range 時,ref 字段爲 NULL。

index: 表示全索引掃描(full index scan), 和 ALL 類型相似,只不過 ALL 類型是全表掃描, 而 index 類型則是掃描全部的索引記錄, 而不掃描數據

index 類型一般會出如今覆蓋索引中,所要查詢的數據直接在索引中就能夠訪問, 而不用回表掃描數據. 此時Extra 字段 會顯示 Using index。

還有一種是全表掃描時經過索引順序訪問數據。此時並不會在Extra提示 using index。

ALL: 表示執行計劃選擇全表掃描,除非數據量極少好比100之內(別擡槓問'101能夠嗎',遇到太高併發count 1000行數據把數據庫堵住的),當執行計劃出現type 爲all 時,咱們儘可能經過修改索引的方式讓查詢利用索引。

3.5 possible_keys

possible_keys 表示 MySQL 在查詢時, 可以使用到的索引. 注意, 即便有些索引在 possible_keys 中出現,可是並不表示此索引會真正地被 MySQL 使用到. MySQL 在查詢時具體使用了哪些索引, 由 key 字段決定。

3.6 key

此字段是 MySQL 在當前查詢時所真正使用到的索引。

3.7 key_len

key_len表示執行計劃所選擇的索引長度有多少字節,一般咱們可藉此判斷聯合索引有多少列被選擇

在這裏 key_len 大小的計算規則是:

通常地,key_len 等於索引列類型字節長度,例如int類型爲4 bytes,bigint爲8 bytes;

若是是字符串類型,還須要同時考慮字符集因素,例如:CHAR(30) UTF8則key_len至少是90 bytes;

若該列類型定義時容許NULL,其key_len還須要再加 1 bytes;

若該列類型爲變長類型,例如 VARCHAR(TEXT\BLOB不容許整列建立索引,若是建立部分索引也被視爲動態列類型),其key_len還須要再加 2 bytes;

id 爲bigint 是8個字節 故key_len=8

表a的字符集爲utf8,name='lisi' name 爲varchar(50) key_len=50*3+2=152 。

3.8 rows

rows 也是一個重要的字段。 MySQL 查詢優化器根據統計信息,估算 SQL 要查找到結果集須要掃描讀取的數據行數。原則上 rows 越少越好。記住這個並不是是徹底準確的值。

3.9 extra

顧名思義 ,該列會提示優化執行計劃的額外的信息,值得你們關注的有以下幾種:

Using index

當 extra 中出現 Using index 時,表示該sql利用覆蓋索引掃描,也即從只訪問索引便可獲取到所需的數據,而不用回表。

Using where

當 extra 中出現 Using where時,表示該sql 回表獲取數據了。什麼是回表呢? 其實就是僅僅經過訪問索引不能知足獲取所需的數據,須要訪問表的page 頁。

若是和Using index 同時出現,說明where條件經過索引定位數據,而後回表,再過濾所須要的數據。

Using filesort

出現 using filesort 說明排序沒有利用索引而發生了額外排序 ,伴隨着的可能還有Using temporary; Using filesort 同時用到臨時表排序。

其實還有其餘一些 提示 Using MRR,Using index condition ,Using index for group-by 等這些提示是正向的,說明sql比較優化。

四 總結

本文基於案例解釋如何理解explain的執行結果,但願對各位須要評估sql執行計劃的朋友有所幫助。

相關文章
相關標籤/搜索