MySQL8 EXPLAIN 命令輸出的都是什麼東西?這篇超詳細!

引子

小扎剛畢業不久,在一家互聯網公司工作,由於是新人,做的也都是簡單的CRUD。剛來的時候還有點不適應,做了幾個月之後,就變成了熟練工了,左複製,右粘貼,然後改改就是自己的代碼了,生活真美好。

有一天,領導說他做的有個列表頁面速度很慢,半天打不開,讓小扎去優化下。小扎心裏一驚,我都是複製別人的代碼,怎麼還有錯?趕緊去問問同事小會,小會說:你先用EXPLAIN命令分析下SQL,看看有什麼問題。

小扎趕緊用EXPLAIN命令跑了一下SQL:
在這裏插入圖片描述
“這些都是什麼東西?”,小扎望着小會,一臉懵逼。。。“能不能給我講講?”

看着小扎無助的眼神,小會無奈:我仔細給你講講吧,你看這裏的輸出。。。



EXPLAIN的輸出列

EXPLAIN命令用來提供MySQL的執行信息,用來顯示SQL語句執行的效率,平時我們發現某條SQL語句執行慢,可以通過該語句來查看原因,看看是否用到了索引,以及其他優化措施。 EXPLAIN 可以應用在 SELECT, DELETE, INSERT, REPLACE, 和 UPDATE 語句上。

EXPLAIN命令的輸出信息如下表所示:

Column JSON Name Meaning 備註
id select_id The SELECT identifier 查詢語句的序號
select_type None The SELECT type 選擇類型(詳見下表)
table table_name The table for the output row 表名
partitions partitions The matching partitions 分區表信息,沒有分區表則爲NULL
type access_type The join type 連接類型(詳見下表)
possible_keys possible_keys The possible indexes to choose 可供選擇的索引
key key The index actually chosen 實際選擇的索引
key_len key_length The length of the chosen key 選擇的索引的長度
ref ref The columns compared to the index 和索引匹配的列
rows rows Estimate of rows to be examined 估算的掃描行數
filtered filtered Percentage of rows filtered by table condition 被條件過濾行數的百分比
Extra None Additional information 額外信息(詳情點擊超鏈接)

小扎,深吸一口氣,怎麼這複雜。。。
這還是總體上的表格,小會笑着,指着上面的 select_type(選擇類型)說,你看這個select_type還可以單獨細分成下面這張表格呢:



select_type(選擇類型)

select_type Value JSON Name Meaning 備註
SIMPLE None Simple SELECT (not using UNION or subqueries) 簡單查詢(沒有使用聯合和子查詢)
PRIMARY None Outermost SELECT 查詢中若包含任何複雜的子部分,最外層的select被標記爲PRIMARY
UNION None Second or later SELECT statement in a UNION UNION中第二個或後面的SELECT語句
DEPENDENT UNION dependent (true) Second or later SELECT statement in a UNION, dependent on outer query UNION中的第二個或後面的SELECT語句,取決於外面的查詢
UNION RESULT union_result Result of a UNION. UNION的結果
SUBQUERY None First SELECT in subquery 子查詢中的第一個SELECT
DEPENDENT SUBQUERY dependent (true) First SELECT in subquery, dependent on outer query 子查詢中的第一個SELECT,取決於外面的查詢
DERIVED None Derived table 派生表的SELECT
DEPENDENT DERIVED dependent (true) Derived table dependent on another table 依賴其他表的派生表的SELECT
MATERIALIZED materialized_from_subquery Materialized subquery 物化子查詢
UNCACHEABLE SUBQUERY cacheable (false) A subquery for which the result cannot be cached and must be re-evaluated for each row of the outer query 不能被緩存的子查詢
UNCACHEABLE UNION cacheable (false) The second or later select in a UNION that belongs to an uncacheable subquery (see UNCACHEABLE SUBQUERY) UNION中第二個或後面的不能被緩存的子查詢

小扎已經開始頭大了,小會說,你不用全記住,等你用到的時候,查一下表格就行,這些都是官方文檔上的資料。
第一張表格中的type(連接類型),還可以細分成以下情況:



type(連接類型)

注意,以下連接類型的查詢速度從快到慢排序


system

The table has only one row (= system table). This is a special case of the const join type.

當表只有一條數據的時候,是const的特例(我只有一條數據,我查找最快:)。

const

The table has at most one matching row, which is read at the start of the query. Because there is only one row, values from the column in this row can be regarded as constants by the rest of the optimizer. const tables are very fast because they are read only once.

const is used when you compare all parts of a PRIMARY KEY or UNIQUE index to constant values. In the following queries, tbl_name can be used as a const table:

通過主鍵或唯一索引查找,搜索結果只有一條數據,速度最快(除system外)。

// 通過主鍵查找
SELECT * FROM tbl_name WHERE primary_key=1;

// 通過唯一索引查找
SELECT * FROM tbl_name
  WHERE primary_key_part1=1 AND primary_key_part2=2;

eq_ref

One row is read from this table for each combination of rows from the previous tables. Other than the system and const types, this is the best possible join type. It is used when all parts of an index are used by the join and the index is a PRIMARY KEY or UNIQUE NOT NULL index.

eq_ref can be used for indexed columns that are compared using the = operator. The comparison value can be a constant or an expression that uses columns from tables that are read before this table. In the following examples, MySQL can use an eq_ref join to process ref_table:

表連接查詢,主鍵索引或者唯一索引全部被命中,是除system和const之外,最好的連接類型,和索引列比較只能使用=號。
查詢結果只有一條數據。

// 多表關聯查詢,單行匹配
SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column=other_table.column;

// 多表關聯查詢,聯合唯一索引,單行匹配
SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column_part1=other_table.column
  AND ref_table.key_column_part2=1;

ref

All rows with matching index values are read from this table for each combination of rows from the previous tables. ref is used if the join uses only a leftmost prefix of the key or if the key is not a PRIMARY KEY or UNIQUE index (in other words, if the join cannot select a single row based on the key value). If the key that is used matches only a few rows, this is a good join type.

ref can be used for indexed columns that are compared using the = or <=> operator. In the following examples, MySQL can use a ref join to process ref_table:

使用最左前綴匹配索引(索引不是主鍵,也不是唯一索引),匹配到了多行數據,如果只有少量數據,性能也是不錯的哦。
和索引列比較可以使用 = 或 <=> 。
查詢結果有多條數據。

// 根據索引(非主鍵,非唯一索引),多行匹配
SELECT * FROM ref_table WHERE key_column=expr;

// 多表關聯查詢,多行匹配
SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column=other_table.column;

// 多表關聯查詢,聯合索引,多行匹配
SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column_part1=other_table.column
  AND ref_table.key_column_part2=1;

fulltext

The join is performed using a FULLTEXT index.

使用全文索引的時候纔會出現。


ref_or_null

This join type is like ref, but with the addition that MySQL does an extra search for rows that contain NULL values. This join type optimization is used most often in resolving subqueries. In the following examples, MySQL can use a ref_or_null join to process ref_table:

這個查詢類型和ref很像,但是 MySQL 會做一個額外的查詢,來看哪些行包含了NULL。

SELECT * FROM ref_table
  WHERE key_column=expr OR key_column IS NULL;

index_merge

This join type indicates that the Index Merge optimization is used. In this case, the key column in the output row contains a list of indexes used, and key_len contains a list of the longest key parts for the indexes used.

在一個查詢裏面很有多索引用被用到,可能會觸發index_merge的優化機制。

unique_subquery

This type replaces eq_ref for some IN subqueries of the following form:

unique_subquery和eq_ref不一樣的地方是使用了in的子查詢:

value IN (SELECT primary_key FROM single_table WHERE some_expr)

unique_subquery is just an index lookup function that replaces the subquery completely for better efficiency.

unique_subquery是一個索引查找函數,代替子查詢提高效率。

index_subquery

This join type is similar to unique_subquery. It replaces IN subqueries, but it works for nonunique indexes in subqueries of the following form:

index_subquery和unique_subquery很像,區別是它在子查詢裏使用的是非唯一索引。

value IN (SELECT key_column FROM single_table WHERE some_expr)

range

Only rows that are in a given range are retrieved, using an index to select the rows. The key column in the output row indicates which index is used. The key_len contains the longest key part that was used. The ref column is NULL for this type.

range can be used when a key column is compared to a constant using any of the =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, LIKE, or IN() operators:

通過索引範圍查找多行數據,可以使用=, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, LIKE, 或 IN() 操作符。

// 多行結果
SELECT * FROM tbl_name
  WHERE key_column = 10;

// 範圍查找
SELECT * FROM tbl_name
  WHERE key_column BETWEEN 10 and 20;
  
// 範圍查找
SELECT * FROM tbl_name
  WHERE key_column IN (10,20,30);
  
// 範圍查找
SELECT * FROM tbl_name
  WHERE key_part1 = 10 AND key_part2 IN (10,20,30);

index

The index join type is the same as ALL, except that the index tree is scanned. This occurs two ways:

If the index is a covering index for the queries and can be used to satisfy all data required from the table, only the index tree is scanned. In this case, the Extra column says Using index. An index-only scan usually is faster than ALL because the size of the index usually is smaller than the table data.

A full table scan is performed using reads from the index to look up data rows in index order. Uses index does not appear in the Extra column.

MySQL can use this join type when the query uses only columns that are part of a single index.

index類型和ALL類型一樣,區別就是index類型是掃描的索引樹。以下兩種情況會觸發:

  1. 如果索引是查詢的覆蓋索引,就是說查詢的數據在索引中都能找到,只需掃描索引樹,不需要回表查詢。 在這種情況下,explain 的 Extra 列的結果是 Using index。僅索引掃描通常比ALL快,因爲索引的大小通常小於表數據。

  2. 全表掃描會按索引的順序來查找數據行。使用索引不會出現在Extra列中。

ALL

A full table scan is done for each combination of rows from the previous tables. This is normally not good if the table is the first table not marked const, and usually very bad in all other cases. Normally, you can avoid ALL by adding indexes that enable row retrieval from the table based on constant values or column values from earlier tables.

全表掃描,效率最低的查詢,一般可以通過添加索引避免,這種情況是不能容忍的,趕緊優化吧。

在這裏插入圖片描述

type(連接類型)在圖中的位置越上面越好,如果實在不行,index也是勉強可以接受的,當然ALL是不可接受的,一定要優化。

小會指着小扎的分析結果說,你看第一列的type是ALL,是最差的連接類型,後面的rows是掃描的行數,進行了全表掃描,肯定會很慢,你條件裏爲什麼用like?
在這裏插入圖片描述
小扎羞愧得臉一紅,我這裏的代碼是從別人那直接複製過來的,忘了改這裏了。。。

小扎趕緊改了下SQL,重新運行:
在這裏插入圖片描述

“現在快多了,type是const耶,我拿到了第二名哦”,小扎對着屏幕開心得笑着。

小會彷彿看到了多年前的自己,頓時思緒萬千,看着他開心的樣子,微微一笑,悄悄地走開了。

參考資料:MySQl官方文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章