談談關於MySQL explain 的詳解

 ebaefd3569c40655a78b678dbc92979b.jpeg

當咱們寫下一條複雜的sql時,不是盲目去執行,選擇提早explain分析sql,是一個不錯的選擇,避免沒用到索引或者用到錯誤的索引致使mysql大量的掃表,形成線上很差的後果。
mysql提供的explain命令能夠獲取select語句的執行計劃,經過explain咱們能夠知道:表的讀取順序,數據讀取操做的類型,哪些索引可使用,哪些索引實際使用了等等。mysql

mysql> explain select * from user;
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | index | NULL          | name | 402     | NULL |    1 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

如下所有基於mysql5.7.32sql

mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.32    |
+-----------+
1 row in set (0.00 sec)
id
mysql> explain select * from user a left join user_info b on a.id=b.user_id where a.id=1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | a     | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL  |
|  1 | SIMPLE      | b     | NULL       | ref   | user_id       | user_id | 8       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+

id相同的時候,從上往下順序執行服務器

mysql> explain select * from user where id = (select user_id from user_info where age=10);
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | PRIMARY     | user      | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL        |
|  2 | SUBQUERY    | user_info | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  |    1 |   100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+

id不一樣的時候,越大的id越先執行,對於這種子查詢,優先執行子sql拿到結果,纔去執行主sqlide

mysql> explain select * from user  union  select * from user;
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type  | table      | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
|  1 | PRIMARY      | user       | NULL       | index | NULL          | name | 402     | NULL |    1 |   100.00 | Using index     |
|  2 | UNION        | user       | NULL       | index | NULL          | name | 402     | NULL |    1 |   100.00 | Using index     |
| NULL | UNION RESULT | <union1,2> | NULL       | ALL   | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)

對於union查詢,會建立一個臨時表,對應的id是null優化

select_type

SIMPLE

simple是簡單查詢,mysql認爲比較簡單的查詢,不包含子查詢和union查詢都是simple,哪怕簡單的join都是simple搜索引擎

mysql> explain select * from user;
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | index | NULL          | name | 402     | NULL |    1 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from user a left join user_info b on a.id=b.user_id;
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref       | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+------+----------+-------------+
|  1 | SIMPLE      | a     | NULL       | index | NULL          | name    | 402     | NULL      |    1 |   100.00 | Using index |
|  1 | SIMPLE      | b     | NULL       | ref   | user_id       | user_id | 8       | test.a.id |    1 |   100.00 | NULL        |
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

PRIMARY

查詢中若包含任何複雜的子部分,最外層查詢則被標記爲primary編碼

mysql> explain select * from user where id = (select id from user_info where age=10);
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | PRIMARY     | user      | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL        |
|  2 | SUBQUERY    | user_info | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  |    1 |   100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain select * from  user union all select * from user;
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
|  1 | PRIMARY     | user  | NULL       | index | NULL          | name | 402     | NULL |    1 |   100.00 | Using index |
|  2 | UNION       | user  | NULL       | index | NULL          | name | 402     | NULL |    1 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

SUBQUERY

在SELECT或WHERE列表中包含了子查詢的語句spa

mysql> explain select (select id from user) from user;
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
|  1 | PRIMARY     | user  | NULL       | index | NULL          | name | 402     | NULL |    1 |   100.00 | Using index |
|  2 | SUBQUERY    | user  | NULL       | index | NULL          | name | 402     | NULL |    1 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain select * from user where id= (select user_id from user_info where age=10);
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | PRIMARY     | user      | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL        |
|  2 | SUBQUERY    | user_info | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  |    1 |   100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

DERIVED

派生表的select(from子句的子查詢)code

mysql> explain select * from (select * from user union select * from user) c;
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type  | table      | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
|  1 | PRIMARY      | <derived2> | NULL       | ALL   | NULL          | NULL | NULL    | NULL |    4 |   100.00 | NULL            |
|  2 | DERIVED      | user       | NULL       | index | NULL          | name | 1023    | NULL |    1 |   100.00 | Using index     |
|  3 | UNION        | user       | NULL       | index | NULL          | name | 1023    | NULL |    1 |   100.00 | Using index     |
| NULL | UNION RESULT | <union2,3> | NULL       | ALL   | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
4 rows in set, 1 warning (0.00 sec)

UNION

union中後面的select語句server

mysql> explain select * from user union select * from user;
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type  | table      | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
|  1 | PRIMARY      | user       | NULL       | index | NULL          | name | 1023    | NULL |    1 |   100.00 | Using index     |
|  2 | UNION        | user       | NULL       | index | NULL          | name | 1023    | NULL |    1 |   100.00 | Using index     |
| NULL | UNION RESULT | <union1,2> | NULL       | ALL   | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
3 rows in set, 1 warning (0.02 sec)

DEPENDENT UNION

出如今union或union all語句中,可是這個查詢要受到外部查詢的影響

mysql> explain select * from user where id in(select id from user union select id from user);
+----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type        | table      | partitions | type   | possible_keys | key     | key_len | ref  | rows | filtered | Extra                    |
+----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+--------------------------+
|  1 | PRIMARY            | user       | NULL       | index  | NULL          | name    | 1023    | NULL |    1 |   100.00 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | user       | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | func |    1 |   100.00 | Using index              |
|  3 | DEPENDENT UNION    | user       | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | func |    1 |   100.00 | Using index              |
| NULL | UNION RESULT       | <union2,3> | NULL       | ALL    | NULL          | NULL    | NULL    | NULL | NULL |     NULL | Using temporary          |
+----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+--------------------------+
4 rows in set, 1 warning (0.11 sec)

DEPENDENT SUBQUERY

同DEPENDENT UNION 差很少,包含子查詢,且受到外部查詢的影響

mysql> explain select * from user where id in(select id from user union select id from user);
+----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type        | table      | partitions | type   | possible_keys | key     | key_len | ref  | rows | filtered | Extra                    |
+----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+--------------------------+
|  1 | PRIMARY            | user       | NULL       | index  | NULL          | name    | 1023    | NULL |    1 |   100.00 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | user       | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | func |    1 |   100.00 | Using index              |
|  3 | DEPENDENT UNION    | user       | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | func |    1 |   100.00 | Using index              |
| NULL | UNION RESULT       | <union2,3> | NULL       | ALL    | NULL          | NULL    | NULL    | NULL | NULL |     NULL | Using temporary          |
+----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+--------------------------+
4 rows in set, 1 warning (0.11 sec)

UNION RESULT

出如今union或union all中,表示的是一個結果集

mysql> explain select id from user union select id from user;
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type  | table      | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
|  1 | PRIMARY      | user       | NULL       | index | NULL          | name | 1023    | NULL |    1 |   100.00 | Using index     |
|  2 | UNION        | user       | NULL       | index | NULL          | name | 1023    | NULL |    1 |   100.00 | Using index     |
| NULL | UNION RESULT | <union1,2> | NULL       | ALL   | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)
table

查詢的表名,有時候顯示的並非真正的表名

mysql> explain select * from (select * from user union select * from user) c;
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type  | table      | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
|  1 | PRIMARY      | <derived2> | NULL       | ALL   | NULL          | NULL | NULL    | NULL |    4 |   100.00 | NULL            |
|  2 | DERIVED      | user       | NULL       | index | NULL          | name | 1023    | NULL |    1 |   100.00 | Using index     |
|  3 | UNION        | user       | NULL       | index | NULL          | name | 1023    | NULL |    1 |   100.00 | Using index     |
| NULL | UNION RESULT | <union2,3> | NULL       | ALL   | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+------+---------+------+------+----------+-----------------+
4 rows in set, 1 warning (0.00 sec)

好比id=1的table是derived2,說明它查的是一個派生表,其中derived[num],這個num就是第幾步執行的結果,這裏是2,說明就是id=2的那一步執行的結果。

partitions

查詢匹配記錄的分區。對於非分區表,值爲NULL。
先建立一張分區表

CREATE TABLE users (
     id INT NOT NULL ,
     name varchar(100) NOT NULL 
)
PARTITION BY RANGE (id) (
     PARTITION p0 VALUES LESS THAN (10),
     PARTITION p1 VALUES LESS THAN (20),
     PARTITION p2 VALUES LESS THAN (30),
     PARTITION p3 VALUES LESS THAN MAXVALUE
);

經過id分了4個區,插入兩條數據

mysql> select * from users;
+----+-------+
| id | name  |
+----+-------+
|  1 | Tom   |
| 11 | jerry |
+----+-------+
2 rows in set (0.00 sec)
mysql> explain select * from users where id=1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | users | p0         | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

id=1 分佈在p0分區

mysql> explain select * from users where id=11;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | users | p1         | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

id=11 分佈在p1分區

type

type是很是重要的指標,它表示咱們使用什麼類型去查數據。下面由好到壞的介紹各個type類型

system

這種類型通常不會出現,官方解釋:

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

表只有一行記錄,通常是系統表,是一種特殊的const類型

const

表最多有一個匹配行,由於只有一行,因此優化器的其他部分能夠將此行中列的值視爲常量。const表很是快,由於它們只讀取一次。

mysql> explain select * from user where id=1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | user  | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

const必定是用到primary key或者unique的索引時候纔會出現。

mysql> explain select * from user where name="Tom";
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | ref  | name          | name | 1023    | const |    1 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

雖然經過name也查出一條數據,可是因爲name是普通索引,因此不是const

eq_ref

除了system和const 它是最好的。通常出如今鏈接查詢中,且鏈接的條件是主鍵索引或者惟一索引時,就會用到它。

mysql> explain select * from user a left join user_info b on a.id=b.user_id;
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref       | rows | filtered | Extra       |
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+
|  1 | SIMPLE      | a     | NULL       | index  | NULL          | name    | 1023    | NULL      |    1 |   100.00 | Using index |
|  1 | SIMPLE      | b     | NULL       | eq_ref | user_id       | user_id | 4       | test.a.id |    1 |   100.00 | NULL        |
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

a.id是primary key
b.user_id是 unique key
先查a表,a表關聯b表經過id和user_id來,由於id是主鍵索引,而user_id又是惟一索引,那麼a表中的每條記錄僅會關聯b表的一條記錄。因此b表的類型就是eq_ref。

ref

和eq_ref相比,不一樣的就是關聯表查詢的字段不是惟一或者主鍵索引,就是匹配到了,還要繼續匹配,可能有多條記錄。

mysql> explain select b.* from user a left join user_info b on a.id=b.user_id;
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref       | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+------+----------+-------------+
|  1 | SIMPLE      | a     | NULL       | index | NULL          | name    | 402     | NULL      |    1 |   100.00 | Using index |
|  1 | SIMPLE      | b     | NULL       | ref   | user_id       | user_id | 8       | test.a.id |    1 |   100.00 | NULL        |
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+------+----------+-------------+
2 rows in set, 1 warning (0.01 sec)

a.id是primary key
b.user_id是普通的索引key
a可能關聯b的多條記錄,可是起碼有索引,因此的b的type就是ref。

fulltext

全文索引的話,innodb不支持,隨着各類搜索引擎的出現,通常出現須要全文索引的地方,都會用相似es擅長分詞的存儲。

ref_or_null

這種類型相似於ref,可是MySQL會額外搜索包含空值的行。

mysql> explain select * from user_info where user_id is null or user_id=1;
+----+-------------+-----------+------------+-------------+---------------+---------+---------+-------+------+----------+-----------------------+
| id | select_type | table     | partitions | type        | possible_keys | key     | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+-----------+------------+-------------+---------------+---------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | user_info | NULL       | ref_or_null | user_id       | user_id | 9       | const |  108 |   100.00 | Using index condition |
+----+-------------+-----------+------------+-------------+---------------+---------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

user_id是普通的索引key,且容許爲null。

index_merge

索引合併

mysql> explain select * from user_info where id=1 or user_id=999;
+----+-------------+-----------+------------+-------------+-----------------+-----------------+---------+------+------+----------+-------------------------------------------+
| id | select_type | table     | partitions | type        | possible_keys   | key             | key_len | ref  | rows | filtered | Extra                                     |
+----+-------------+-----------+------------+-------------+-----------------+-----------------+---------+------+------+----------+-------------------------------------------+
|  1 | SIMPLE      | user_info | NULL       | index_merge | PRIMARY,user_id | PRIMARY,user_id | 8,9     | NULL |    2 |   100.00 | Using union(PRIMARY,user_id); Using where |
+----+-------------+-----------+------------+-------------+-----------------+-----------------+---------+------+------+----------+-------------------------------------------+
1 row in set, 1 warning (0.00 sec)

id是主鍵索引,user_id是普通索引,結果用了兩個索引的合併

unique_subquery

用於where中的in形式子查詢,子查詢返回不重複值惟一值,能夠徹底替換子查詢,效率更高。 該類型替換了下面形式的IN子查詢的ref。

explain select * from user_info where user_id in (select id from user where id>10);

子查詢的id爲primary key或者unique key

index_subquery

相似於unique_subquery子查詢,可是子查詢返回的是非惟一索引。

explain select * from user_info where user_id in (select id from user where id>10);

子查詢的id不是primary keyunique key

range

索引範圍掃描,通常條件使用了 >,<,between,in等運算符的查詢。

mysql> explain select * from user where id>=1 and id <=100;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | range | PRIMARY       | PRIMARY | 8       | NULL |  100 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

index

和全表掃差很少,可是隻有索引樹被掃描,這一般比全表掃快,由於索引文件一般比數據文件小。

mysql> explain select user_id from user_info;
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | user_info | NULL       | index | NULL          | user_id | 9       | NULL | 100100 |   100.00 | Using index |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

ALL

全表掃描

mysql> explain select * from user_info where age=1;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | user_info | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 100100 |    10.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

age沒有索引,引擎層作了全表掃。

possible_keys

查詢可能使用到的索引。

mysql> explain select * from user_info where id>1 and user_id >2;
+----+-------------+-----------+------------+-------+-----------------+---------+---------+------+-------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys   | key     | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+-----------+------------+-------+-----------------+---------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | user_info | NULL       | range | PRIMARY,user_id | PRIMARY | 8       | NULL | 50050 |    50.00 | Using where |
+----+-------------+-----------+------------+-------+-----------------+---------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

iduser_id都有索引

key

執行器最終選擇的索引

mysql> explain select * from user_info where id>1 and user_id >2;
+----+-------------+-----------+------------+-------+-----------------+---------+---------+------+-------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys   | key     | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+-----------+------------+-------+-----------------+---------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | user_info | NULL       | range | PRIMARY,user_id | PRIMARY | 8       | NULL | 50050 |    50.00 | Using where |
+----+-------------+-----------+------------+-------+-----------------+---------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

這裏選擇了主鍵索引。

key_len

使用的索引的長度,這裏面的狀況仍是挺複雜的,特別對於一些複合索引。
假設存在這樣的表

CREATE TABLE `testlen` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name1` varchar(10) not null default "",
  `name2` varchar(10),
  `num1` int(10) not null default 0,
  `num2` int(10),
   PRIMARY KEY (`id`),
   key(`name1`),
   key(`name2`),
   key(`num1`),
   key(`num2`)
   
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

1.當字段爲定長時,如char,int這些,須要有一個字節來標記是否爲空,not null的話就不須要。

mysql> explain select * from testlen where num1=1;
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | testlen | NULL       | ref  | num1          | num1 | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.03 sec)
mysql> explain select * from testlen where num2=1;
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | testlen | NULL       | ref  | num2          | num2 | 5       | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

2.當字段爲變長的時候,如varchar這些,除了是否須要用一個字節來標記非not null的,還須要額外的兩個字節標記長度

3.對於char、varchar這些,utf8編碼的一個字符佔用3個字節,utf8mb4一個字符佔用4個字節

758d3abb60a427ae44809a63ad530247.png

mysql> explain select * from testlen where name1='1';
+----+-------------+---------+------------+------+---------------+-------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key   | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+-------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | testlen | NULL       | ref  | name1         | name1 | 42      | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+-------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

對於utf8mb4編碼的來講,not null的varchar(10) 最終長度=10*4+2 = 42

mysql> explain select * from testlen where name2='1';
+----+-------------+---------+------------+------+---------------+-------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key   | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+-------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | testlen | NULL       | ref  | name2         | name2 | 43      | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+-------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

對於utf8mb4編碼的來講,容許爲null的varchar(10) 最終長度=10*4+2+1 = 43。

ref

這一列顯示了在key列記錄的索引中,表查找值所用到的列或常量。

mysql> explain select * from user where id=1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | user  | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

where 條件是id=1,那麼對應的ref列就是常量const。

mysql> explain select * from user a left join user_info b on a.id=b.user_id;
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref       | rows | filtered | Extra       |
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+
|  1 | SIMPLE      | a     | NULL       | index  | NULL          | name    | 1023    | NULL      |    1 |   100.00 | Using index |
|  1 | SIMPLE      | b     | NULL       | eq_ref | user_id       | user_id | 4       | test.a.id |    1 |   100.00 | NULL        |
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+
2 rows in set, 1 warning (0.02 sec)

user_info關聯user表,關聯的條件是user.id,故ref=test.a.id(test是庫名)

rows

掃描出的行數,這個是個估算的值,並非真正的結果集

filtered

filtered表示返回結果的行數佔需讀取行數的百分比,filtered列的值依賴於統計信息。

extra

一些額外的信息,主要說明如何找到數據的。

using index

使用覆蓋索引的時候就會出現,只查找索引列的值。

mysql> explain select user_id from user_info;
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | user_info | NULL       | index | NULL          | user_id | 4       | NULL |    2 |   100.00 | Using index |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

using where

MYSQL服務器層將在存儲引擎層返回行之後再應用WHERE過濾條件,通常發生在不能走索引掃描的狀況下或者走索引掃描,可是有些查詢列不在索引當中的狀況下。

mysql> explain select * from user_info where user_id>10;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | user_info | NULL       | ALL  | user_id       | NULL | NULL    | NULL | 100100 |    50.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

Using temporary

對於一些order by、group by可能會用到臨時表。

mysql> explain select * from user a left join user_info b on a.id=b.user_id where a.id>=1 order by b.user_id;
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+------+----------+----------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref       | rows | filtered | Extra                                        |
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+------+----------+----------------------------------------------+
|  1 | SIMPLE      | a     | NULL       | range | PRIMARY       | PRIMARY | 8       | NULL      | 5187 |   100.00 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | b     | NULL       | ref   | user_id       | user_id | 9       | test.a.id |    1 |   100.00 | Using where                                  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+------+----------+----------------------------------------------+
2 rows in set, 1 warning (0.01 sec)

Using filesort

通常order by相關的沒用到索引,就要文件排序。

mysql> explain select * from user_info order by age desc;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra          |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
|  1 | SIMPLE      | user_info | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 100100 |   100.00 | Using filesort |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+

Using index condition

相比using where,Using index condition把where條件用到的索引放在引擎層過濾一下,沒用到索引的列,在server層再過濾一遍。

mysql> explain select * from user a left join user_info b on a.id=b.user_id where b.user_id>1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+-------+----------+-----------------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref       | rows  | filtered | Extra                 |
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+-------+----------+-----------------------+
|  1 | SIMPLE      | a     | NULL       | index | PRIMARY       | name    | 402     | NULL      | 10375 |   100.00 | Using index           |
|  1 | SIMPLE      | b     | NULL       | ref   | user_id       | user_id | 9       | test.a.id |     1 |   100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+---------+---------+-----------+-------+----------+-----------------------+
2 rows in set, 1 warning (0.00 sec)

 

相關文章
相關標籤/搜索