MySQL 查詢性能分析之 Explain

EXPLAIN 關鍵字能夠用於獲取 SQL 語句執行計劃的相關信息,在 MySQL 8.0 中,EXPLAIN 支持大多數 SQL 語句,如 SELECT 、DELETE 、INSERT 、REPLACE、和 UPDATE 。示例以下:html

mysql> EXPLAIN SELECT * FROM employees;
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------+
|  1 | SIMPLE      | employees | ALL  | NULL          | NULL | NULL    | NULL | 299379 | NULL  |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------+
複製代碼

注:本篇文章的測試數據來源於 MySQL 官方提供的 Employees Sample Database,其數據庫結構以下:mysql

如下分別介紹 EXPLAIN 輸出結果中各個字段的含義:git

1. id

id 爲行標識符,同時也表示語句執行的優先級,值越大則優先級越高。特殊狀況下,若是某行語句引用了其餘多行結果集的並集,則該值能夠爲 NULL。示例以下:github

# 該FROM字句只是用於演示,並無任何實際意義
mysql> EXPLAIN SELECT COUNT(1) FROM (SELECT emp_no FROM salaries) AS t;
+----+-------------+------------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table      | type  | possible_keys | key     | key_len | ref  | rows    | Extra       |
+----+-------------+------------+-------+---------------+---------+---------+------+---------+-------------+
|  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL    | NULL    | NULL | 2757635 | NULL        |
|  2 | DERIVED     | salaries   | index | NULL          | PRIMARY | 7       | NULL | 2757635 | Using index |
+----+-------------+------------+-------+---------------+---------+---------+------+---------+-------------+
複製代碼
# 查詢工資大於500000或部門編號等於d007的全部僱員的編號
mysql> EXPLAIN SELECT emp_no FROM salaries WHERE salary>500000 UNION ALL SELECT emp_no FROM dept_emp WHERE dept_no = "d007";
+----+--------------+------------+------+---------------+---------+---------+-------+---------+--------------------------+
| id | select_type  | table      | type | possible_keys | key     | key_len | ref   | rows    | Extra                    |
+----+--------------+------------+------+---------------+---------+---------+-------+---------+--------------------------+
|  1 | PRIMARY      | salaries   | ALL  | NULL          | NULL    | NULL    | NULL  | 2837161 | Using where              |
|  2 | UNION        | dept_emp   | ref  | dept_no       | dept_no | 4       | const |   91566 | Using where; Using index |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL          | NULL    | NULL    | NULL  |    NULL | Using temporary          |
+----+--------------+------------+------+---------------+---------+---------+-------+---------+--------------------------+
複製代碼

2. select_type

select_type 用於表示查詢的類型,常見類型及其含義以下:sql

  • SIMPLE:不包含子查詢或者 UNION 操做的查詢;
  • PRIMARY:查詢中若是包含任何子查詢,那麼最外層的查詢則被標記爲 PRIMARY ;
  • SUBQUERY:子查詢中第一個 SELECT ;
  • DEPENDENT SUBQUERY:子查詢中的第一個 SELECT,取決於外部查詢;
  • UNION:UNION 操做的第二個或者以後的查詢;
  • DEPENDENT UNION:UNION 操做的第二個或者以後的查詢,取決於外部查詢;
  • UNION RESULT:UNION 產生的結果集;
  • DERIVED:出如今 FROM 字句中的子查詢。

這裏以查詢類型爲 SUBQUERY 的狀況進行演示,示例以下:shell

# 根據員工編號查詢員工姓名及其工資總和
mysql> EXPLAIN SELECT first_name,(SELECT sum(salary) FROM salaries WHERE emp_no = 10001) FROM employees WHERE emp_no = 10001;
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table     | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
|  1 | PRIMARY     | employees | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
|  2 | SUBQUERY    | salaries  | ref   | PRIMARY       | PRIMARY | 4       | const |   17 | NULL  |
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
複製代碼

3. table

表示語句執行的目標表,除了正常的表名或表別名外,還會出現如下取值:數據庫

  • <unionM,N>:輸出結果中編號爲 M 的行與編號爲 N 的行的結果集的並集。
  • <derivedN>:輸出結果中編號爲 N 的行的結果集,derived 表示這是一個派生結果集,如 FROM 子句中的查詢。
  • <subqueryN>:輸出結果中編號爲 N 的行的結果集,subquery 表示這是一個物化子查詢。

4. type

type 字段是進行性能判斷的重要依據,它表示 MySQL 使用何種方式來查找目標數據集,不一樣查找方式會致使不一樣的性能開銷,常見查找方式及其性能表現按照由高到低的順序排序以下:bash

1. system:這是 const 類型的一個特例,只會出如今待查詢的表只有一行數據的狀況下。服務器

2. const:常出如今主鍵或惟一索引與常量值進行比較的場景下,此時查詢性能是最優的。性能

mysql> EXPLAIN SELECT * FROM employees WHERE emp_no = 10008;
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table     | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | employees | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
複製代碼

3. eq_ref:當鏈接使用的是完整的索引而且是 PRIMARY KEY 或 UNIQUE NOT NULL INDEX 時使用它。

# 這裏員工部門關係表 dept_no 的聯合主鍵爲 emp_no + dept_no ,即員工編號+部門標號
mysql> EXPLAIN SELECT * FROM employees e,dept_emp d WHERE e.emp_no = d.emp_no AND dept_no = "d005";
+----+-------------+-------+--------+-----------------+---------+---------+--------------------+------+-----------------------+
| id | select_type | table | type   | possible_keys   | key     | key_len | ref                | rows | Extra                 |
+----+-------------+-------+--------+-----------------+---------+---------+--------------------+------+-----------------------+
|  1 | SIMPLE      | d     | ref    | PRIMARY,dept_no | dept_no | 4       | const              |    1 | Using index condition |
|  1 | SIMPLE      | e     | eq_ref | PRIMARY         | PRIMARY | 4       | employees.d.emp_no |    1 | NULL                  |
+----+-------------+-------+--------+-----------------+---------+---------+--------------------+------+-----------------------+
複製代碼

4. ref:當鏈接使用的是前綴索引或鏈接條件不是 PRIMARY KEY 或 UNIQUE INDEX 時則使用它。

# 這裏僅使用了前綴索引emp_no,因此其類型爲 ref , 而不是 eq_ref
mysql> EXPLAIN SELECT * FROM employees e,dept_emp d WHERE e.emp_no = d.emp_no;
+----+-------------+-------+------+---------------+---------+---------+--------------------+--------+-------+
| id | select_type | table | type | possible_keys | key     | key_len | ref                | rows   | Extra |
+----+-------------+-------+------+---------------+---------+---------+--------------------+--------+-------+
|  1 | SIMPLE      | e     | ALL  | PRIMARY       | NULL    | NULL    | NULL               | 299379 | NULL  |
|  1 | SIMPLE      | d     | ref  | PRIMARY       | PRIMARY | 4       | employees.e.emp_no |      1 | NULL  |
+----+-------------+-------+------+---------------+---------+---------+--------------------+--------+-------+
複製代碼

5. ref_or_null:相似於 ref 類型的查詢,可是附加了對 NULL 值列的查詢。示例語句以下:

SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;
複製代碼

6. index_merge:該聯接類型表示使用了索引進行合併優化,示例以下:

mysql> EXPLAIN SELECT * FROM dept_emp WHERE dept_no = "d004" AND emp_no < 10020;
+----+-------------+----------+-------------+-----------------+-----------------+---------+------+------+-----------------------------------------------+
| id | select_type | table    | type        | possible_keys   | key             | key_len | ref  | rows | Extra                                         |
+----+-------------+----------+-------------+-----------------+-----------------+---------+------+------+-----------------------------------------------+
|  1 | SIMPLE      | dept_emp | index_merge | PRIMARY,dept_no | dept_no,PRIMARY | 8,4     | NULL |    1 | Using intersect(dept_no,PRIMARY); Using where |
+----+-------------+----------+-------------+-----------------+-----------------+---------+------+------+-----------------------------------------------+
複製代碼

7. range:使用索引進行範圍掃描,常見於 between、> 、< 這樣的查詢條件。

mysql> EXPLAIN SELECT * FROM employees WHERE emp_no > 10000;
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
| id | select_type | table     | type  | possible_keys | key     | key_len | ref  | rows   | Extra       |
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
|  1 | SIMPLE      | employees | range | PRIMARY       | PRIMARY | 4       | NULL | 149689 | Using where |
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
複製代碼

8. index:索引鏈接類型與 ALL 相同,只是掃描的是索引樹,一般出如今索引是該查詢的覆蓋索引的狀況:

mysql> EXPLAIN SELECT emp_no FROM employees;
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
| id | select_type | table     | type  | possible_keys | key     | key_len | ref  | rows   | Extra       |
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
|  1 | SIMPLE      | employees | index | NULL          | PRIMARY | 4       | NULL | 299379 | Using index |
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
複製代碼

9. ALL:全表掃描,效率最差的查找方式。

mysql> EXPLAIN SELECT * FROM employees WHERE first_name ="Bezalel";
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | employees | ALL  | NULL          | NULL | NULL    | NULL | 299379 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
複製代碼

5. possible_keys

表示在執行過程當中可能會用到哪些索引來進行優化。

6. key

表示在執行過程當中實際用到的用於優化的索引。

7. key_len

表示使用到的索引的字節數。

8. ref

顯示哪些列或常量與 key 列中指定的索引進行比較。

9. rows

MySQL 爲了找到目標行而讀取的全部行的數量,這是一個估算的值。

10. Extra

Extra 列主要用於顯示額外的信息,常見信息及其含義以下:

1. Using where :MySQL 服務器會在存儲引擎檢索行後再進行過濾。示例以下:

# first_name 字段是一個普通的列,非索引列
mysql> EXPLAIN SELECT * FROM employees WHERE first_name = "Sumant";
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | employees | ALL  | NULL          | NULL | NULL    | NULL | 299379 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
複製代碼

2. Using filesort:一般出如今 GROUP BY 或 ORDER BY 語句中,且排序或分組沒有基於索引,此時須要使用文件在內存中進行排序。由於使用索引排序的性能好於使用文件排序,因此出現這種狀況能夠考慮經過添加索引進行優化。示例以下:

mysql> EXPLAIN SELECT * FROM employees ORDER BY first_name ;
+----+-------------+-----------+------+---------------+------+---------+------+--------+----------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra          |
+----+-------------+-----------+------+---------------+------+---------+------+--------+----------------+
|  1 | SIMPLE      | employees | ALL  | NULL          | NULL | NULL    | NULL | 299379 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+--------+----------------+
複製代碼

3. Using index:使用了覆蓋索引進行查詢,此時不須要訪問表,從索引中就能夠獲取到所需的所有數據。示例以下:

mysql> EXPLAIN SELECT emp_no FROM employees;
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
| id | select_type | table     | type  | possible_keys | key     | key_len | ref  | rows   | Extra       |
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
|  1 | SIMPLE      | employees | index | NULL          | PRIMARY | 4       | NULL | 299379 | Using index |
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
複製代碼

4. Using temporary:表示須要使用臨時表來處理查詢,常出如今 GROUP BY 或 ORDER BY 語句中,示例以下:

mysql> EXPLAIN SELECT first_name,count(first_name) FROM employees GROUP BY first_name ;
+----+-------------+-----------+------+---------------+------+---------+------+--------+---------------------------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra                           |
+----+-------------+-----------+------+---------------+------+---------+------+--------+---------------------------------+
|  1 | SIMPLE      | employees | ALL  | NULL          | NULL | NULL    | NULL | 299379 | Using temporary; Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+--------+---------------------------------+
複製代碼

參考資料

更多參數的說明能夠參考 MySQL 官方文檔:dev.mysql.com/doc/refman/…

更多文章,歡迎訪問 [全棧工程師手冊] ,GitHub 地址:github.com/heibaiying/…

相關文章
相關標籤/搜索