這篇文章將給你們介紹如何使用 explain 來分析一條 sql 。mysql
網上其實已經有很是多的文章都很詳細的介紹了 explain 的使用,這篇文章將實例和原理結合起來,儘可能讓你有更好的理解,相信我,認真看完你應該會有特別的收穫。sql
explain 翻譯過來就是解釋的意思, 在 mysql 裏被稱做執行計劃,便可以經過該命令看出 mysql 在通過優化器分析後決定要如何執行該條 sql 。bash
說到優化器,再多說一句,mysql 內置了一個強大的優化器,優化器的主要任務就是把你寫的 sql 再給優化一下,儘量以更低成本去執行,好比掃描更少的行數,避免排序等。執行一條sql語句都經歷了什麼? 我在前面的文章中有介紹過優化器相關的。測試
你可能會問,通常在何時會要用 explain 呢,大多數狀況下都是從 mysql 的慢查詢日誌中揪出來一些查詢效率比較慢的 sql 來使用 explain 分析,也有的是就是在對 mysql 進行優化的時候,好比添加索引,經過 explain 來分析添加的索引可否被命中,還有的就是在業務開發的時候,在知足需求的狀況下,你可能須要經過 explain 來選擇一個更高效的 sql。優化
那麼 explain 該怎麼用呢,很簡單,直接在 sql 前面加上 explain 就好了,以下所示。ui
mysql> explain select * from t;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
| 1 | SIMPLE | t | ALL | NULL | NULL | NULL | NULL | 100332 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
1 row in set (0.04 sec)
複製代碼
能夠看到,explain 會返回約 10 個字段,不一樣版本返回的字段有些許差別,每一個字段都表明着具體的意義,這篇文章我不打算把每一個字段都詳細的介紹一遍,東西比較多,怕你也不容易記住,不如先把幾個重要的字段好好理解了。spa
其中 type、key、rows、Extra 這幾個字段我認爲是比較重要的,咱們接下來經過具體的實例來幫你更好的理解這幾個字段的含義。翻譯
首先有必要簡單介紹下這幾個字段的字面意思。日誌
type 表示 mysql 訪問數據的方式,常見的有全表掃描(all)、遍歷索引(index)、區間查詢(range)、常量或等值查詢(ref、eq_ref)、主鍵等值查詢(const)、當表中只有一條記錄時(system)。下面是效率從最好到最差的一個排序。code
system > const > eq_ref > ref > range > index > all
複製代碼
key 表示查詢過程實際會用到的索引名稱。
rows 表示查詢過程當中可能須要掃描的行數,這個數據不必定準確,是mysql 抽樣統計的一個數據。
Extra 表示一些額外的信息,一般會顯示是否使用了索引,是否須要排序,是否會用到臨時表等。
好了,接下來正式開始實例分析。
仍是沿用前面文章中建立的存儲引擎建立一個測試表,咱們這裏插入 10 w 條測試數據,表結構以下:
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
複製代碼
而後看下面這條查詢語句,注意這個表目前只有一個主鍵索引,尚未建立普通索引。
mysql> explain select * from t;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
| 1 | SIMPLE | t | ALL | NULL | NULL | NULL | NULL | 100332 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
1 row in set (0.04 sec)
複製代碼
其中 type 值爲 ALL,表示全表掃描了,你們注意看到 rows 這個字段顯示有 100332 條,實際上咱們一共才 10w 條數據,因此這個字段只是 mysql 的一個預估,並不必定準確。這種全表掃描的效率很是低,是須要重點被優化的。
接下來咱們分別給字段 a 和 b 添加普通索引,而後再看下添加索引後的幾條 sql 。
mysql> alter table t add index a_index(a);
Query OK, 0 rows affected (0.19 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table t add index b_index(b);
Query OK, 0 rows affected (0.20 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from t;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| t | 0 | PRIMARY | 1 | id | A | 100332 | NULL | NULL | | BTREE | | |
| t | 1 | a_index | 1 | a | A | 100332 | NULL | NULL | YES | BTREE | | |
| t | 1 | b_index | 1 | b | A | 100332 | NULL | NULL | YES | BTREE | | |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)
複製代碼
mysql> explain select * from t where a > 1000;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | t | ALL | a_index | NULL | NULL | NULL | 100332 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)
複製代碼
上面這條 sql 看起來是否是有點疑惑呢,type 居然顯示剛剛不是給字段 a 添加索引了麼,並且 possible_keys 也顯示了有 a_index 可用,可是 key 顯示 null,表示 mysql 實際上並不會使用 a 索引,這是爲啥?
這裏是由於 select * 的話還須要回到主鍵索引上查找 b 字段,這個過程叫回表,這條語句會篩選出 9w 條知足條件的數據,也就是說這 9w 條數據都須要回表操做,全表掃描都才 10w 條數據,因此在 mysql 的優化器看來還不如直接全表掃描得了,至少還免去了回表過程了。
固然也不是說只要有回表操做就不會命中索引,用不用索引關鍵還在於 mysql 認爲哪一種查詢代價更低,咱們把上面的 sql 中 where 條件再稍微改造一下。
mysql> explain select * from t where a > 99000;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+
| 1 | SIMPLE | t | range | a_index | a_index | 5 | NULL | 999 | Using index condition |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+
1 row in set (0.00 sec)
複製代碼
這回 type 值爲 range 了,key 爲 a_index ,表示命中了 a 索引,是一個不錯的選擇,是由於知足這條 sql 條件的只有 1000 條數據,mysql 認爲 1000 條數據就算回表也要比全表掃描的代價低,因此說 mysql 實際上是個很聰明的傢伙。
咱們還能夠看到 Extra 字段中值爲 Using index condition,這個意思是指用到了索引,可是須要回表,再看下面這個語句。
mysql> explain select a from t where a > 99000;
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
| 1 | SIMPLE | t | range | a_index | a_index | 5 | NULL | 999 | Using where; Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
複製代碼
這個 Extra 中的值爲 Using where; Using index ,表示查詢用到了索引,且要查詢的字段在索引中就能拿到,不須要回表,顯然這種效率比上面的要高,因此不要輕易寫 select * ,只查詢業務須要的字段便可,這樣能夠儘量避免回表。
再來看一個須要排序的。
mysql> explain select a from t where a > 99000 order by b;
+----+-------------+-------+-------+---------------+---------+---------+------+------+---------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+---------------------------------------+
| 1 | SIMPLE | t | range | a_index | a_index | 5 | NULL | 999 | Using index condition; Using filesort |
+----+-------------+-------+-------+---------------+---------+---------+------+------+---------------------------------------+
1 row in set (0.00 sec)
複製代碼
這個 Extra 中返回了一個 Using filesort,意味着須要排序,這種是須要重點優化的的,也就是說查到數據後,還須要 mysql 在內存中對其進行排序,你要知道索引自己就是有序的,因此通常來說要儘可能利用索引的有序性,好比像下面這樣寫。
mysql> explain select a from t where a > 99990 order by a;
+----+-------------+-------+-------+------------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+------------------+---------+---------+------+------+--------------------------+
| 1 | SIMPLE | t | range | a_index,ab_index | a_index | 5 | NULL | 10 | Using where; Using index |
+----+-------------+-------+-------+------------------+---------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
複製代碼
咱們再建立一個複合索引看看。
mysql> alter table t add index ab_index(a,b);
Query OK, 0 rows affected (0.19 sec)
Records: 0 Duplicates: 0 Warnings: 0
複製代碼
mysql> explain select * from t where a > 1000;
+----+-------------+-------+-------+------------------+----------+---------+------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+------------------+----------+---------+------+-------+--------------------------+
| 1 | SIMPLE | t | range | a_index,ab_index | ab_index | 5 | NULL | 50166 | Using where; Using index |
+----+-------------+-------+-------+------------------+----------+---------+------+-------+--------------------------+
1 row in set (0.00 sec)
複製代碼
這條 sql 剛剛在上面也有講到過,在沒有建立複合索引的時候,是走的全表掃描,如今實際上是利用了覆蓋索引,一樣是免去了回表過程,即在 (ab_index) 索引上就能找出要查詢的字段。
這篇文章經過幾個實例介紹瞭如何使用 explain 分析一條 sql 的執行計劃,也提到了一些常見的索引優化,事實上還有更多的可能性,你也能夠本身去寫一個 sql ,而後使用 explain 分析,看看有哪些是能夠被優化的。
這篇文章我斷斷續續寫了有三四天了,原本準備了更多的例子,但每次都是寫了一部分,思路也打亂了,好了,有問題歡迎在下面留言交流,文章對你有幫助,點個贊表示鼓勵支持。