Foreword:
A query statement generates a so-called after various cost-based and rule-based optimization sessions of the MySQL query optimizer 执行计划
. This execution plan shows the specific way to execute the query next, such as the order of multi-table joins. For each What access method the table uses to specifically execute the query, and so on. MySQL
It provides us with EXPLAIN
statements to help us view the specific execution plan of a certain query statement. The content of this chapter is to help you understand what EXPLAIN
each output item of the statement is for, so that we can improve the performance of our query statement in a targeted manner. performance.
Table of contents
- 1. The EXPLAIN execution plan contains the information of each column
-
- 1.1 table
- 1.2 id
- 1.3 select_type
- 1.4 partitions
- 1.5 type
- 1.6 possible_keys和key
- 1.7 key_len
- 1.8 ref
- 1.9 rows
- 1.10 filtered
- 1.11 Extra
-
- 1.11.1 No tables used
- 1.11.2 No matching min/max row
- 1.11.3 Using index
- 1.11.4 Using index condition
- 1.11.5 Using where
- 1.11.6 Using join buffer (Block Nested Loop)
- 1.11.7 Not exists
- 1.11.8 Using intersect(...)、Using union(...)和Using sort_union(...)
- 1.11.9 Zero limit
- 1.11.10 Using filesort
- 1.11.11 Using temporary
- 1.11.12 Start temporary, End temporary
- 1.11.13 LooseScan
- 1.11.14 FirstMatch(tbl_name)
- 2. Execution plan in Json format
- 三、Extented EXPLAIN
1. The EXPLAIN execution plan contains the information of each column
If we want to see the execution plan of a certain query, we can add EXPLAIN before the specific query statement, like this:
mysql> EXPLAIN SELECT 1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set, 1 warning (0.00 sec)
Then a lot of things that are output are so-called 执行计划
. My task is to lead everyone to understand what each column in this big thing is for, and with the 执行计划
help of this, how should we improve ourselves? Query statement to make query execution more efficient. In fact, except for the query statement starting with , you can add this word in front of SELECT
the rest DELETE
, INSERT
, REPLACE
and statements to view the execution plan of these statements, but we are more interested in the SELECT statement here, so we will only use the statement as an example later Describes the usage of the statement. In order to let everyone have a perceptual understanding first, let's roughly list the functions of each column output by the statement:UPDATE
EXPLAIN
SELECT
EXPLAIN
EXPLAIN
column name | describe |
---|---|
id | Each SELECT keyword in a large query statement corresponds to a unique id |
select_type | The type of query corresponding to the SELECT keyword |
table | Table Name |
partitions | Matching Partition Information |
type | Access method for single table |
possible_keys | Indexes that may be used |
key | the index actually used |
key_len | The actual index length used |
ref | When using the index column equivalent query, the object information for equivalent matching with the index column |
rows | Estimated number of records that need to be read |
filtered | Percentage of the number of remaining records after a table is filtered by search conditions |
Extra | some additional information |
It should be noted that if you can’t understand the meaning of the above output columns, it’s normal, so don’t get entangled~. I list them here just to describe an outline, so that everyone can have a general impression. We have used s1
the table sum n times before s2
, in order to prevent everyone from forgetting, describe its structure again:
mysql> create table s1 (
id int not null auto_increment,
key1 varchar(100),
key2 int,
key3 varchar(100),
key_part1 varchar(100),
key_part2 varchar(100),
key_part3 varchar(100),
common_field varchar(100),
primary key (id),
key idx_key1 (key1),
unique key idx_key2 (key2),
key idx_key3 (key3),
key idx_key_part(key_part1, key_part2, key_part3));
Query OK, 0 rows affected (0.04 sec)
mysql> create table s2 (
id int not null auto_increment,
key1 varchar(100),
key2 int,
key3 varchar(100),
key_part1 varchar(100),
key_part2 varchar(100),
key_part3 varchar(100),
common_field varchar(100),
primary key (id),
key idx_key1 (key1),
unique key idx_key2 (key2),
key idx_key3 (key3),
key idx_key_part(key_part1, key_part2, key_part3));
Query OK, 0 rows affected (0.04 sec)
mysql> insert into s1 select * from demo8;
Query OK, 20000 rows affected (0.83 sec)
Records: 20000 Duplicates: 0 Warnings: 0
mysql> insert into s2 select * from demo8;
Query OK, 20000 rows affected (0.89 sec)
Records: 20000 Duplicates: 0 Warnings: 0
The structure of my two tables s1
is the same, and there are records s2
in these two tables , and random values are inserted in the other columns except for the id column. 20000
In order to let everyone have a better reading experience, we are not going to EXPLAIN
introduce the purpose of these columns in strict accordance with the order of the output columns. Just pay attention to it.
1.1 table
No matter how complex our query statement is, how many tables are contained in it, in the end, we need to perform single-table access to each table, so it is MySQL
stipulated EXPLAIN语句输出的每条记录对应着某个单表的访问方法,该条记录的table列代表着该表的表名
. So let's look at a relatively simple query statement:
mysql> EXPLAIN SELECT * FROM s1;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
1 row in set, 1 warning (0.00 sec)
This query statement only involves s1
a single-table query of the table, so EXPLAIN
there is only one record in the output, and table
the value of the column is s1
, indicating that this record is used to illustrate the s1
single-table access method to the table.
Let's take a look at the execution plan of a join query:
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------------------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | Using join buffer (hash join) |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------------------------+
2 rows in set, 1 warning (0.01 sec)
It can be seen that there are two records in the execution plan of this connection query. The columns of these two records table
are s1
and respectively s2
. These two records are used to explain the access method of s1
the table and the table respectively.s2
1.2 id
We know that the query statements we write generally SELECT
start with a keyword, and there is only one keyword in a relatively simple query statement SELECT
, such as the following query statement:
SELECT * FROM s1 WHERE key1 = 'aa';
SELECT
There is only one keyword in a slightly more complicated connection query , such as:
SELECT * FROM s1 INNER JOIN s2
ON s1.key1 = s2.key1
WHERE s1.common_field = '7869101';
However, multiple keywords may appear in a query statement in the following two cases SELECT
:
Case 1:子查询
Cases included in the query
For example, the following query contains 2 SELECT
keywords:
SELECT * FROM s1 WHERE key1 IN (SELECT * FROM s2);
Case 2: The case where the query contains UNION
a statement
For example, the following query statement also contains 2 SELECT
keywords:
SELECT * FROM s1 UNION SELECT * FROM s2;
Every time a keyword appears in a query statement SELECT
, MySQL assigns it a unique id
value. This id
value is EXPLAIN
the first column of the statement. For example, there is only one SELECT
keyword in the query below, so there is only one record EXPLAIN
in the result :id
1
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'aa';
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 403 | const | 67 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
For join queries, multiple tables can be followed in the clauses SELECT
after a keyword FROM
, so in the execution plan of join queries, , 每个表都会对应一条记录,但是这些记录的id值都是相同的
for example:
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------------------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | Using join buffer (hash join) |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------------------------+
2 rows in set, 1 warning (0.00 sec)
s1
It can be seen that in the above connection query, the and tables participating in the connection s2
correspond to one record respectively, but the corresponding id
values of these two records are both 1
. What everyone needs to remember here is that 在连接查询的执行计划中,每个表都会对应一条记录,这些记录的id列的值是相同的,出现在前边的表表示驱动表,出现在后边的表表示被驱动表
. So from the output above EXPLAIN
, we can see that the query optimizer is going to use s1
the table as the driving table and s2
the table as the driven table to execute the query.
For a query statement containing a subquery, multiple keywords may be involved SELECT
, so in the execution plan of a query statement containing a subquery, each SELECT
keyword will correspond to a unique id
value, such as this:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'aa';
+----+-------------+-------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| 1 | PRIMARY | s1 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 20250 | 100.00 | Using where |
| 2 | SUBQUERY | s2 | NULL | index | idx_key1 | idx_key1 | 403 | NULL | 20250 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
From the output results, we can see that s1
the table is in the outer query, and the outer query has an independent keyword, so the value SELECT
of the first record is that the table is in the subquery, and the subquery has an independent keyword, so The value of the second record is .id
1
s2
SELECT
id
2
But everyone needs to pay special attention here 查询优化器可能对涉及子查询的查询语句进行重写,从而转换为连接查询
. So if we want to know whether the query optimizer has rewritten a statement containing a subquery, just look at the execution plan directly, for example:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key3 FROM s2 WHERE common_field = 'a');
+----+--------------+-------------+------------+--------+---------------------+---------------------+---------+----------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+-------------+------------+--------+---------------------+---------------------+---------+----------------+-------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | ALL | idx_key1 | NULL | NULL | NULL | 20250 | 100.00 | Using where |
| 1 | SIMPLE | <subquery2> | NULL | eq_ref | <auto_distinct_key> | <auto_distinct_key> | 403 | testdb.s1.key1 | 1 | 100.00 | NULL |
| 2 | MATERIALIZED | s2 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 20250 | 10.00 | Using where |
+----+--------------+-------------+------------+--------+---------------------+---------------------+---------+----------------+-------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)
id
It can be seen that although our query statement is a subquery, the value of the third record in the execution plan 2
indicates that this record corresponds to a single-table query. From its select_type
value MATERIALIZED
, we can see that the query optimizer is To convert the subquery into a materialized table first. id
Then look at the values of the first two records in the execution plan 1
, indicating that the tables corresponding to these two records are connected and queried. It should be noted that table
the value of the column of the second record is <subquery2>
, indicating that the table is actually id
the corresponding 2
subquery The materialized table generated after execution, and then the s1
join query will be performed with the materialized table.
For queries containing UNION clauses, it is true that each SELECT
keyword corresponds to a value, but there is still something special, such as the following query:id
mysql> EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
+----+--------------+------------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
| 1 | PRIMARY | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 2 | UNION | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 3 | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)
What is the third record in the execution plan of this statement? Is it weird that Mao table
Lie grows? Don't forget UNION
what the clause is for. It will combine the result sets of multiple queries and deduplicate the records in the result set. How to deduplicate? MySQL uses internal temporary tables. As shown in the query plan above, UNION
the clause is to combine the query id
for 1
and the result set of the query id
for 2
and deduplicate, so a <union1, 2>
temporary table named (that is, the third record of the execution plan) is created internally table column name), id
to 3
indicate that this temporary table was created to combine the result sets of two queries.
Compared with UNION, UNION ALL does not need to deduplicate the final result set. It simply merges the records in the result sets of multiple queries into one and returns it to the user, so there is no need to use a temporary table. So in the execution plan of the query that contains the UNION ALL clause, there is no record with the id of 3, as shown below:
mysql> EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
| 1 | PRIMARY | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 2 | UNION | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
1.3 select_type
Through the above content, we know that a large query statement can contain several SELECT
keywords, each SELECT
keyword represents a small query statement, and each SELECT
keyword FROM
clause can contain several tables (these tables Used for join query), each table corresponds to a record in the execution plan output, and for SELECT
tables in the same keyword, their id
values are the same.
Each small query represented by the SELECT keyword in MySQL defines an attribute called select_type, which means that as long as we know the select_type attribute of a small query, we know what this small query plays in the entire large query. The role is unreliable, let's first see what values this select_type can take (for the sake of accuracy, we directly use the English in the document for a brief description, and then explain it in detail):
name | describe |
---|---|
SIMPLE |
Simple SELECT (not using UNION or subqueries) |
PRIMARY |
Outermost SELECT |
UNION |
Second or later SELECT statement in a UNION |
UNION RESULT |
Result of a UNION |
SUBQUERY |
First SELECT in subquery |
DEPENDENT SUBQUERY |
First SELECT in subquery, dependent on outer query |
DEPENDENT UNION |
Second or later SELECT statement in a UNION, dependent on outerquery |
DERIVED |
Derived table |
MATERIALIZED |
Materialized subquery |
UNCACHEABLE SUBQUERY |
A subquery for which the result cannot be cached and must be re——evaluated for each row of the outer query |
UNCACHEABLE UNION |
The second or later select in a UNION that belongs to an uncacheable subquery (see UNCACHEABLE SUBQUERY) |
English description is too simple, do not know what to say? Let’s take a closer look at what each value inside is for:
1.3.1 SIMPLE
UNION
Queries that do not contain or in the query statement 子查询
are counted as SIMPLE
types. For example, select_type
the value of the single-table query below is SIMPLE
:
mysql> EXPLAIN SELECT * FROM s1;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
1 row in set, 1 warning (0.00 sec)
Of course, connection queries are also SIMPLE
types, such as:
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------------------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | Using join buffer (hash join) |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------------------------+
2 rows in set, 1 warning (0.00 sec)
1.3.2 PRIMARY
For a large query containing UNION
, UNION ALL
or , it is composed of several small queries, where the value 子查询
of the leftmost query is , for example:select_type
PRIMARY
mysql> EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
+----+--------------+------------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
| 1 | PRIMARY | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 2 | UNION | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 3 | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)
As can be seen from the results, the leftmost small query SELECT * FROM s1
corresponds to the first record in the execution plan, and its select_type
value is PRIMARY
.
1.3.3 UNION
For a large query containing UNION
or UNION ALL
, it is composed of several small queries. Except for the leftmost small query, the select_type
value of the rest of the small queries is UNION
, which can be compared with the effect of the previous example. example.
1.3.4 UNION RESULT
MySQL chooses to use a temporary table to complete UNION
the deduplication work of the query. The query for the temporary table select_type
is UNION RESULT
the example above, so I won’t go into details.
1.3.5 SUBQUERY
If the query statement containing the subquery cannot be converted into the corresponding semi-join
form, 该子查询是不相关子查询
and the query optimizer decides to execute the subquery by materializing the subquery, the first SELECT
keyword of the subquery represents the The query select_type
is SUBQUERY
, for example, the following query:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'aa';
+----+-------------+-------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| 1 | PRIMARY | s1 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 20250 | 100.00 | Using where |
| 2 | SUBQUERY | s2 | NULL | index | idx_key1 | idx_key1 | 403 | NULL | 20250 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
As you can see, the outer query select_type
is PRIMARY
, and the subquery select_type
is SUBQUERY
. It should be noted that because select_type
the SUBQUERY
subquery for will be materialized, it only needs to be executed once.
1.3.6 DEPENDENT SUBQUERY
If the corresponding form of 包含子查询
the query statement , and the subquery is a correlated subquery, then the query represented by the first keyword of the subquery is , for example, the following query:不能够转为
semi-join
SELECT
select_type
DEPENDENT
SUBQUERY
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2 WHERE s1.key2 = s2.key2) OR key3 = 'a';
+----+--------------------+-------+------------+--------+-------------------+----------+---------+----------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+-------+------------+--------+-------------------+----------+---------+----------------+-------+----------+-------------+
| 1 | PRIMARY | s1 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 20250 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | s2 | NULL | eq_ref | idx_key2,idx_key1 | idx_key2 | 5 | testdb.s1.key2 | 1 | 10.00 | Using where |
+----+--------------------+-------+------------+--------+-------------------+----------+---------+----------------+-------+----------+-------------+
2 rows in set, 2 warnings (0.00 sec)
It should be noted that select_type
the DEPENDENT SUBQUERY
query may be executed multiple times.
1.3.7 DEPENDENT UNION
In a large query containing UNION
or UNION ALL
, if each small query depends on the outer query, except for the leftmost small query, the select_type
value of the remaining small queries is DEPENDENT UNION
. It's a bit confusing, for example, the query below:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2 WHERE key1 = 'aa' UNION SELECT key1
FROM s1 WHERE key1 = 'b');
+----+--------------------+------------+------------+------+---------------+----------+---------+-------+-------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+------------+------------+------+---------------+----------+---------+-------+-------+----------+--------------------------+
| 1 | PRIMARY | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | s2 | NULL | ref | idx_key1 | idx_key1 | 403 | const | 67 | 100.00 | Using where; Using index |
| 3 | DEPENDENT UNION | s1 | NULL | ref | idx_key1 | idx_key1 | 403 | const | 1 | 100.00 | Using where; Using index |
| 4 | UNION RESULT | <union2,3> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------------+------------+------------+------+---------------+----------+---------+-------+-------+----------+--------------------------+
4 rows in set, 1 warning (0.01 sec)
This query is more complicated. The big query contains a subquery, and the subquery is UNION
connected by two small queries. It can be seen from the execution plan that SELECT key1 FROM s2WHERE key1 = 'aa'
this small query is the first query in the subquery, so it select_type
is DEPENDENT SUBQUERY
, and SELECT key1 FROM s1 WHERE key1 ='b'
this query select_type
isDEPENDENT UNION
1.3.8 DERIVED
For a query that includes a derived table that is executed in a materialized manner, the subquery corresponding to the derived table select_type
is DERIVED
, for example, the following query:
mysql> EXPLAIN SELECT * FROM (SELECT key1, count(*) as c FROM s1 GROUP BY key1) AS derived_s1 where c > 1;
+----+-------------+------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 2 | DERIVED | s1 | NULL | index | idx_key1 | idx_key1 | 403 | NULL | 20250 | 100.00 | Using index |
+----+-------------+------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
It can be seen from the execution plan that the record of id
for 2
represents the execution method of the subquery, and it select_type
means DERIVED
that the subquery is executed in a materialized manner. id
The record for 1
represents the outer layer query. Please pay attention to see that its table column shows yes <derived2>
, indicating that the query is for the table after the derived table is materialized.
小提示:
If the derived table can be executed by merging with the outer query, the execution plan is another scene, you can try it~
1.3.9 MATERIALIZED
select_type
When the query optimizer executes a statement containing a subquery and chooses to materialize the subquery and perform a join query with the outer query, the attributes corresponding to the subquery are, for example, the following query MATERIALIZED
:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2);
+----+--------------+-------------+------------+--------+---------------------+---------------------+---------+----------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+-------------+------------+--------+---------------------+---------------------+---------+----------------+-------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | ALL | idx_key1 | NULL | NULL | NULL | 20250 | 100.00 | Using where |
| 1 | SIMPLE | <subquery2> | NULL | eq_ref | <auto_distinct_key> | <auto_distinct_key> | 403 | testdb.s1.key1 | 1 | 100.00 | NULL |
| 2 | MATERIALIZED | s2 | NULL | index | idx_key1 | idx_key1 | 403 | NULL | 20250 | 100.00 | Using index |
+----+--------------+-------------+------------+--------+---------------------+---------------------+---------+----------------+-------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)
id
The value of the third record in the execution plan 2
indicates that this record corresponds to a single-table query. From its select_type
value MATERIALIZED
, it can be seen that the query optimizer needs to convert the subquery first 物化表
. id
Then look at the values of the first two records in the execution plan 1
, indicating that the tables corresponding to these two records are connected and queried. It should be noted that table
the value of the column of the second record is <subquery2>
, indicating that the table is actually id
the corresponding 2
subquery The materialized table generated after execution, and then the s1
join query will be performed with the materialized table.
1.3.10 UNCACHEABLE SUBQUERY
I don't use it often, so I won't talk too much
1.3.11 UNCACHEABLE UNION
I don't use it often, so I won't talk too much.
1.4 partitions
Since we haven't talked about what partitioning is at all, we won't talk about this output column. Under normal circumstances, the partitions
values of the columns in the execution plan of our query statements are all NULL
.
1.5 type
We said earlier that a record in the execution plan represents the access method used by MySQL to execute a query on a table, and the type column in it indicates what the access method is. For example, the following query:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'aa';
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 403 | const | 67 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
You can see type
that the value of the column is ref
, indicating that the access method MySQL
is about to be used to execute the query on the table. But we only talked about some access methods for single-table access to tables using storage engines. The complete access methods are as follows: , , , , , , , , , , , L. Of course we have to go into more detail:ref
s1
InnoDB
system
const
eq_ref
ref
fulltext
ref_or_null
index_merge
unique_subquery
index_subquery
range
index
AL
1.5.1 system
When there is only one record in the table and 该表使用的存储引擎的统计数据是精确的,比如MyISAM、Memory
, then the access method of the table is system
. Let's say we create a new MyISAM
table and insert a record into it:
mysql> CREATE TABLE t(i int) Engine=MyISAM;
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO t VALUES(1);
Query OK, 1 row affected (0.01 sec)
Then let's look at the execution plan for querying this table:
mysql> EXPLAIN SELECT * FROM t;
+----+-------------+-------+------------+--------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | t | NULL | system | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
You can see type
that the value of the column is system
.
小提示:
You can change the table to use the InnoDB storage engine, and try to see what the type column of the execution plan is.
1.5.2 const
We have talked about this before, that is, when we perform equivalent matching with constants based on the primary key or unique secondary index column, the access method for a single table is, for const
example:
mysql> EXPLAIN SELECT * FROM s1 WHERE id = 5;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
1.5.3 eq_ref
When connecting queries, if the driven table is accessed through the primary key or unique secondary index column equivalence matching (if the primary key or unique secondary index is a joint index, all index columns must be equal value comparison), the access method for the driven table is eq_ref
, for example:
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+-------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+-------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ALL | PRIMARY | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | testdb.s1.id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+-------+----------+-------+
2 rows in set, 1 warning (0.01 sec)
It can be seen from the results of the execution plan that MySQL intends to use s1
the driving table as s2
the driven table, and the key s2
access method is to eq_ref
indicate that the s2
table can be accessed through the equivalent matching of the primary key when accessing the table.
1.5.4 full text
Full-text index, we haven't talked about it in detail, skip it~
1.5.5 ref_or_null
When an equivalent matching query is performed on a common secondary index, and the value of the index column can also be NULL
a value, then the access method of the table may be ref_or_null
, for example:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'aa' OR key1 IS NULL;
+----+-------------+-------+------------+-------------+---------------+----------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+---------------+----------+---------+-------+------+----------+-----------------------+
| 1 | SIMPLE | s1 | NULL | ref_or_null | idx_key1 | idx_key1 | 403 | const | 68 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------------+---------------+----------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
1.5.6 index_merge
In general, only one index can be used for the query of a certain table, but when we talked about the single table access method, we specially emphasized that in some scenarios, we can use, , and Intersection
these Union
three Sort-Union
index merge methods to execute the query, forget it Go back and make up for it. Let’s take a look at how the execution plan reflects how MySQL uses index merging to execute queries on a certain table:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'aa' OR key3 = 'aa';
+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+---------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+---------------------------------------------+
| 1 | SIMPLE | s1 | NULL | index_merge | idx_key1,idx_key3 | idx_key1,idx_key3 | 403,403 | NULL | 68 | 100.00 | Using union(idx_key1,idx_key3); Using where |
+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+---------------------------------------------+
1 row in set, 1 warning (0.01 sec)
From type
the value of the column in the execution plan index_merge
, it can be seen that MySQL intends to use the index merge method to execute s1
the query on the table.
1.5.7 unique_subquery
eq_ref
Similar to the access method of the driven table in the two-table join , unique_subquery
it is aimed at some query statements containing IN subqueries, if the query optimizer decides to convert the IN
subquery to EXISTS
a subquery, and the subquery can use the primary key for equivalent matching If , then type
the value of the column of the subquery execution plan is unique_subquery
, for example, the following query statement:
mysql> EXPLAIN SELECT * FROM s1 WHERE key2 IN (SELECT id FROM s2 where s1.key1 = s2.key1) OR key3 = 'aa';
+----+--------------------+-------+------------+-----------------+------------------+---------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+-------+------------+-----------------+------------------+---------+---------+------+-------+----------+-------------+
| 1 | PRIMARY | s1 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 20250 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | s2 | NULL | unique_subquery | PRIMARY,idx_key1 | PRIMARY | 4 | func | 1 | 10.00 | Using where |
+----+--------------------+-------+------------+-----------------+------------------+---------+---------+------+-------+----------+-------------+
2 rows in set, 2 warnings (0.00 sec)
You can see that the value of the second record of the execution plan type
is , indicating that the index of the column unique_subquery
will be used when executing the subquery .id
1.5.8 index_subquery
index_subquery
Similar to unique_subquery
, except that ordinary indexes are used when accessing tables in subqueries, such as this:
mysql> EXPLAIN SELECT * FROM s1 WHERE common_field IN (SELECT key3 FROM s2 where s1.key1 = s2.key1) OR key3 = 'aa';
+----+--------------------+-------+------------+----------------+-------------------+----------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+-------+------------+----------------+-------------------+----------+---------+------+-------+----------+-------------+
| 1 | PRIMARY | s1 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 20250 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | s2 | NULL | index_subquery | idx_key1,idx_key3 | idx_key3 | 403 | func | 4 | 10.00 | Using where |
+----+--------------------+-------+------------+----------------+-------------------+----------+---------+------+-------+----------+-------------+
2 rows in set, 2 warnings (0.01 sec)
1.5.9 range
If an index is used to obtain records in certain ranges, access methods may be used range
, such as the following query:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN ('a', 'b', 'c');
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | s1 | NULL | range | idx_key1 | idx_key1 | 403 | NULL | 3 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
or:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'a' AND key1 < 'b';
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | s1 | NULL | range | idx_key1 | idx_key1 | 403 | NULL | 1272 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
1.5.10 index
When we can use index coverage but need to scan all index records, the access method of the table is index
, for example:
mysql> EXPLAIN SELECT key_part2 FROM s1 WHERE key_part3 = 'aa';
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+-------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+-------+----------+--------------------------+
| 1 | SIMPLE | s1 | NULL | index | idx_key_part | idx_key_part | 1209 | NULL | 20250 | 10.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+-------+----------+--------------------------+
1 row in set, 1 warning (0.01 sec)
There is only one column in the search list in the above query key_part2
, and there is only key_part3
one column in the search condition, and these two columns happen to be included in idx_key_part
this index, but the search condition key_part3
cannot directly use the index for ref or range
method access, and can only scan The records of the entire idx_key_part
index, so type
the value of the column of the query plan is index
.
小提示:
Once again, for tables using the InnoDB storage engine, the records of the secondary index only contain the values of the index column and the primary key column, while the clustered index contains all user-defined columns and some hidden columns, so scan the secondary index The cost is lower than the direct full table scan, that is, the cost of scanning the clustered index.
1.5.11 ALL
The most familiar full table scan, let’s not talk too much, just look at the example:
mysql> EXPLAIN SELECT * FROM s1;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
1 row in set, 1 warning (0.01 sec)
In general, these access methods perform worse in the order in which we introduce them. Except for All
this access method, all other access methods can use the index, and except for index_merge
the access method, the rest of the access methods can only use at most one index.
1.6 possible_keys和key
In EXPLAIN
the execution plan output by the statement, possible_keys
the columns indicate which indexes may be used when executing a single-table query on a certain table in a certain query statement, and the key
columns indicate which indexes are actually used. For example, the following query:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key3 = 'aa';
+----+-------------+-------+------------+------+-------------------+----------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+-------------------+----------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key1,idx_key3 | idx_key3 | 403 | const | 1 | 5.00 | Using where |
+----+-------------+-------+------------+------+-------------------+----------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)
possible_keys
The value of the column in the above execution plan is idx_key1
, idx_key3
which means that the query may use two indexes idx_key1
, idx_key3
and key
the value of the column is idx_key3
, which means that after the query optimizer calculates the cost of using different indexes, it is finally decided to use it idx_key3
to execute the query is more cost-effective.
But one thing is special, that is, when using the index access method to query a table, possible_keys
the column is empty, but key
the column shows the index actually used, for example:
mysql> EXPLAIN SELECT key_part2 FROM s1 WHERE key_part3 = 'aa';
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+-------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+-------+----------+--------------------------+
| 1 | SIMPLE | s1 | NULL | index | idx_key_part | idx_key_part | 1209 | NULL | 20250 | 10.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+-------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
Another thing to note is that possible_keys
the more values in the column, the better. The more indexes that may be used, the longer it will take for the query optimizer to calculate the query cost, so if possible, try to delete those that are not used. index of.
1.7 key_len
key_len
The column indicates the maximum length of the index record when the optimizer decides to use an index to execute the query, which consists of these three parts:
- For an index column using a fixed-length type, the maximum length of the storage space it actually occupies is the fixed value. For a variable-length index column with a specified character set, for example, the type of an index column is
VARCHAR(100)
, and the character set used isutf8
, then the maximum storage space actually occupied by the column is100 × 3 = 300
bytes. - If the index column can store
NULL
a value, it is 1 byte morekey_len
than when it cannot store a value.NULL
- For variable-length fields, there will be 2 bytes of space to store the actual length of the variable-length column.
For example, the following query:
mysql> EXPLAIN SELECT * FROM s1 WHERE id = 5;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
Since id
the column's type is INT
and cannot store values, the size is NULL
when using the column's index . When indexed columns can store values, such as:key_len
4
NULL
mysql> EXPLAIN SELECT * FROM s1 WHERE key2 = 5;
+----+-------------+-------+------------+-------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | const | idx_key2 | idx_key2 | 5 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
You can see key_len
that the column becomes more 5
than when using the column index . For variable-length index columns, such as the following query:id
1
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'aa';
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 403 | const | 67 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
Since the type of the key1 column is VARCHAR(100)
, the actual maximum storage space occupied by this column is 400
bytes, and because this column allows storage of NULL
values, it key_len
needs to be added 1
, and because this column is 可变长度列
, it key_len
needs to be added 2
, so the final value of ken_len is 403
.
Some students may have doubts: Didn't you say when you were nagging , is n't InnoDB行格式
it possible to store the actual length of the variable-length field ? Why is it used now regardless of three seven twenty one ? What needs to be emphasized here is that the generation of the execution plan is a function in the layer, not for a specific storage engine. MySQL’s output columns in the execution plan are mainly for us to distinguish a query that uses a joint index. Specifically, several index columns are used, not to accurately explain whether the space occupied by a specific storage engine to store the actual length of a variable-length field occupies 1 byte or 2 bytes. For example, consider the following query that uses a joint index :1个字节
2个字节
2个字节
MySQL server
key_len
idx_key_part
mysql> EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'aa';
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key_part | idx_key_part | 403 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
key_len
We can see the value from the column of the execution plan , which means that MySQL can only use one index column of the index 403
in executing the above query , and the following query:idx_key_part
mysql> EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'aa' AND key_part2 = 'b';
+----+-------------+-------+------------+------+---------------+--------------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key_part | idx_key_part | 806 | const,const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
ken_len
The value of the column in the execution plan of this query is , indicating that the two index columns of 806
the joint index can be used when executing this query .idx_key_part
1.8 ref
When using the index column equivalence matching conditions to execute the query, that is, when the access method is one of const
, eq_ref
, ref
, ref_or_null
, unique_subquery
, the column shows what is the equivalent matching index column, for example, it is just a constant or a column. Take a look at this query below:index_subquery
ref
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'aa';
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 403 | const | 67 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
It can be seen ref
that the value of the column is const
, indicating that when using idx_key1
the index to execute the query, key1
the object that matches the equivalent value of the column is a constant. Of course, sometimes it is more complicated:
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+-------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+-------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ALL | PRIMARY | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | testdb.s1.id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+-------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
s2
It can be seen that the access method to the driven table is , and the value of the eq_ref
corresponding column is , which means that the index will be used when accessing the driven table , that is, the condition for the clustered index to perform equivalent matching with a column. The object for equivalence matching in the table is the column (note that the database name is also written here). Sometimes the object for equivalent matching with the index column is a function, for example, the following query:ref
testdb.s1.id
PRIMARY
s2
id
testdb.s1.id
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s2.key1 = UPPER(s1.key1);
+----+-------------+-------+------------+------+---------------+----------+---------+------+-------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+------+-------+----------+-----------------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | ref | idx_key1 | idx_key1 | 403 | func | 79 | 100.00 | Using index condition |
+----+-------------+-------+------------+------+---------------+----------+---------+------+-------+----------+-----------------------+
2 rows in set, 1 warning (0.00 sec)
If we look at the second record of the execution plan, we can see that the access method is used to execute the query s2
on the table , and then the output in the column of the query plan is that the object for equivalent matching with the column of the table is a function.ref
ref
func
s2
key1
1.9 rows
If the query optimizer decides to use a full table scan to execute a query on a table, the rows
column of the execution plan represents the expected number of rows to be scanned. If an index is used to execute the query, the rows
column of the execution plan represents the index that is expected to be scanned Record the number of rows. For example, the following query:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z';
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | s1 | NULL | range | idx_key1 | idx_key1 | 403 | NULL | 1 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
rows
We see that the value of the column of the execution plan is 1
, which means that the query optimizer thinks that there are only one record that idx_key1
satisfies the condition ' after analyzing the cost of using the query .key1 > 'z
1
1.10 filtered
When analyzing the cost of connection queries, a condition filtering
concept was proposed before, which is a strategy adopted by MySQL when calculating the fan-out of driving tables:
- If you are using a single-table query executed by full table scan, you need to estimate how many records meet the search conditions when calculating the fan-out of the driving table.
- If a single-table scan performed by an index is used, when calculating the fan-out of the driving table, it is necessary to estimate how many records satisfy other search conditions except those using the corresponding index.
For example, consider the following query:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND common_field = 'aa';
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+
| 1 | SIMPLE | s1 | NULL | range | idx_key1 | idx_key1 | 403 | NULL | 1 | 10.00 | Using index condition; Using where |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+
key
It can be seen from the columns of the execution plan that the query uses idx_key1
the index to execute the query, and it can be seen from rows
the columns that there are satisfied key1 > 'z'
records 1
. The column of the execution plan filtered
represents the query optimizer predicts 1
how many records in this record meet the rest of the search conditions, that is, common_field = 'aa
the percentage of this condition. filtered
The value of the column here is 10.00
, indicating that the query optimizer predicts that some1
records in the records meet this condition.10.00%
common_field = 'aa'
For single-table queries, filtered
the value of this column is meaningless. We pay more attention to filtered
the value of the execution plan record corresponding to the driving table in the join query. For example, the following query:
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key1 WHERE s1.common_field = 'aa';
+----+-------------+-------+------------+------+---------------+----------+---------+----------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------+-------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | ALL | idx_key1 | NULL | NULL | NULL | 20250 | 10.00 | Using where |
| 1 | SIMPLE | s2 | NULL | ref | idx_key1 | idx_key1 | 403 | testdb.s1.key1 | 79 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------+-------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
It can be seen from the execution plan that the query optimizer intends to treat it s1
as a driving table and s2
as a driven table. We can see that the execution plan of the driving s1
table rows
is listed as 20250
, which means that the fan-out value of the driving table s1 is , which means that about 10 queries will be executed on the driven tablefiltered
10.00
20250× 10.00% = 2025
2025
1.11 Extra
As the name suggests, Extra
columns are used to illustrate some additional information, and we can use this additional information to more accurately understand how MySQL will execute a given query statement. There are dozens of additional information provided by MySQL, and we will not introduce them one by one (I feel that our articles are almost like documents~), so we only select some common or important additional information to introduce to Everyone
1.11.1 No tables used
FROM
This additional information will be prompted when there is no clause in the query statement , such as:
mysql> EXPLAIN SELECT 1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set, 1 warning (0.00 sec)
1.11.2 No matching min/max row
When there is MIN
an or MAX
aggregate function in the query list, but there are no WHERE
records matching the search conditions in the clause, the additional information will be prompted, for example:
mysql> EXPLAIN SELECT MIN(key1) FROM s1 WHERE key1 = 'abcdefg';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No matching min/max row |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------------------+
1 row in set, 1 warning (0.00 sec)
1.11.3 Using index
When our query list and search conditions only contain columns belonging to a certain index, that is, when index coverage can be used, Extra
the additional information will be prompted in the column. For example, the following query only needs to be used idx_key1
and does not need to return to the table:
mysql> EXPLAIN SELECT key1 FROM s1 WHERE key1 = 'a';
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 403 | const | 1 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
1.11.4 Using index condition
Although index columns appear in some search conditions, indexes cannot be used, such as the following query:
SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%a';
Among them, key1 > 'z'
the index can be used, but key1 LIKE '%a'
the index cannot be used. In the previous version of MySQL, the query is executed according to the following steps:
- According to
key1 > 'z'
this condition,idx_key1
the corresponding secondary index record is obtained from the secondary index. - Return to the table according to the primary key value in the secondary index record obtained in the previous step, find the complete user record, check whether the record meets this
key1 LIKE '%a'
condition, and add the qualified record to the final result set.
However, although key1 LIKE '%a'
it is not possible to form a range interval to participate in range
the execution of the access method, this condition only involves key1
columns after all, so MySQL has improved the above steps:
- First, according to
key1 > 'z'
this condition, locateidx_key1
the corresponding secondary index record in the secondary index. - For the specified secondary index record, do not return to the table in a hurry, but first check whether the record meets
key1 LIKE '%a'
this condition. If this condition is not met, there is no need to return the secondary index record to the table at all. key1 LIKE '%a'
Execute the table return operation for the secondary index records that meet this condition.
We say that the table return operation is actually one 随机IO
, which is time-consuming, so although the above modification only improves a little bit, it can save a lot of cost for the table return operation. MySQL calls their improvement 索引条件下推
(English name: Index Condition Pushdown
)
If this feature will be pushed down using the index condition during the execution of the query statement, it will be displayed in the Extra column Using index condition
, for example:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%b';
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | s1 | NULL | range | idx_key1 | idx_key1 | 403 | NULL | 1 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
1.11.5 Using where
When we use a full table scan to execute a query on a table, and WHERE
there are search conditions for the table in the clauses of the statement, Extra
the above additional information will be prompted in the column. For example, the following query:
mysql> EXPLAIN SELECT * FROM s1 WHERE common_field = 'a';
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
When using index access to execute a query on a table, and the WHERE
clauses of the statement have other search conditions than the columns included in the index, Extra
the above additional information will also be prompted in the column. For example, although the query below uses idx_key1
the index to execute the query, the search conditions include not only key1
the search conditions key1 = 'a'
, but also common_field
the search conditions, so Extra
the column will display Using where
the prompt:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' AND common_field = 'a';
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 403 | const | 1 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
1.11.6 Using join buffer (Block Nested Loop)
During the execution of the connection query, when the driven table cannot effectively use the index to speed up the access speed, MySQL will generally allocate a named join buffer
memory block for it to speed up the query speed, which is what we are talking about 基于块的嵌套循环算法
, such as the following query statement:
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.common_field = s2.common_field;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+--------------------------------------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 10.00 | Using where; Using join buffer (hash join) |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+--------------------------------------------+
2 rows in set, 1 warning (0.01 sec)
s2
There are two hints that can be displayed in the Extra column of the execution plan for the table:
Using join buffer (Block Nested Loop)
: This is becauses2
the access to the table cannot effectively use the index, so we have to settle for the next best thing and use itjoin buffer
to reduce the number of accesses to the s2 table, thereby improving performance.Using where
: You can see that there is as1.common_field = s2.common_field
condition in the query statement, becauses1
it is the driving table ands2
the driven table, so whens2
the table is accessed,s1.common_field
the value of the value has been determined, so in facts2
the condition of the query table is , so additional informations2.common_field = 一个常数
is prompted .Using where
1.11.7 Not exists
When we use the left outer join, if the WHERE clause contains a NULL
search condition that requires a certain column of the driven table to be equal to the value, and that column is not allowed to store values, then the columns NULL
in the execution plan of the table will be Extra
Prompt Not exists
additional information, such as this:
mysql> EXPLAIN SELECT * FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE s2.id IS NULL;
+----+-------------+-------+------------+------+---------------+----------+---------+----------------+-------+----------+-------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------+-------+----------+-------------------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | ref | idx_key1 | idx_key1 | 403 | testdb.s1.key1 | 1 | 10.00 | Using where; Not exists |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------+-------+----------+-------------------------+
2 rows in set, 1 warning (0.00 sec)
In the above query s1
, the table is the driving table, s2
the table is the driven table, s2.id
the columns are not allowed to store NULL
values, and the search conditions WHERE
contained in the clauses s2.id IS NULL
mean that the records that must be the driving table cannot be matched in the driven table ON
The record of the clause condition will add the record of the driving table to the final result set, so for a record in the driving table, if a record that meets the clause condition can be found in the driven table, ON
then The records of the driving table will not be added to the final result set, that is to say 我们没有必要到被驱动表中找到全部符合ON子句条件的记录
, this can save a little performance.
小提示:
Right outer joins can be converted to left outer joins, so the case of right outer joins is not mentioned
1.11.8 Using intersect(…)、Using union(…)和Using sort_union(…)
Extra
If there is Using intersect(...)
a prompt in the column of the execution plan Intersect
, it means that the query is going to be executed by index merging. The ... in the brackets indicates the name of the index that needs to be merged; if a prompt appears, it means that the query Using union(...)
is going to be Union
executed by index merging; Using sort_union(...)
, indicating that Sort-Union
the query is to be executed by index merging. For example, the execution plan of this query:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'aa' OR key3 = 'aa';
+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+---------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+---------------------------------------------+
| 1 | SIMPLE | s1 | NULL | index_merge | idx_key1,idx_key3 | idx_key1,idx_key3 | 403,403 | NULL | 68 | 100.00 | Using union(idx_key1,idx_key3); Using where |
+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+---------------------------------------------+
1 row in set, 1 warning (0.00 sec)
Among them, the Extra column is displayed Using union(idx_key1,idx_key3)
, indicating that MySQL is about to use the method of index merge idx_key3
with idx_key1
these two indexes Union
to execute the query.
小提示 :
The remaining two types of index merged Extra column information will not give examples one by one. Write a query yourself~
1.11.9 Zero limit
When the parameter of our LIMIT
clause 0
is , it means that we do not intend to read any records from the table at all, and will prompt for this additional information, such as this:
mysql> EXPLAIN SELECT * FROM s1 LIMIT 0;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Zero limit |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------+
1 row in set, 1 warning (0.00 sec)
1.11.10 Using filesort
In some cases, the index can be used to sort the records in the result set, such as the following query:
mysql> EXPLAIN SELECT * FROM s1 ORDER BY key1 LIMIT 10;
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | index | NULL | idx_key1 | 403 | NULL | 10 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
This query statement can use idx_key1
the index to directly fetch the records key1
of the column 10
, and then return to the table. However, in many cases, the sorting operation cannot use the index, and can only be sorted in memory (when there are few records) or on disk (when there are many records). MySQL sorts this way in memory or on disk Collectively referred to as 文件排序
(English name: filesort
). If a query needs to be executed by file sorting, a prompt will be displayed in the Extra column of the execution plan Using filesort
, for example:
mysql> EXPLAIN SELECT * FROM s1 ORDER BY common_field LIMIT 10;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | Using filesort |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------+
1 row in set, 1 warning (0.00 sec)
It should be noted that if there are a lot of records that need to be sorted by filesort in the query, this process is very performance-consuming. We'd better find a way to change the execution method of using file sorting to use index for sorting.
1.11.11 Using temporary
During the execution of many queries, MySQL may use temporary tables to complete some functions, such as deduplication DISTINCT
and sorting GROUP BY
. UNION
To complete the query, MySQL will most likely seek to execute the query by creating an internal temporary table. If an internal temporary table is used in the query, a prompt Extra
will be displayed in the column of the execution plan Using temporary
, for example:
mysql> EXPLAIN SELECT DISTINCT common_field FROM s1;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | Using temporary |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
1 row in set, 1 warning (0.00 sec)
In addition, it is not a good sign that it appears in the execution plan Using temporary
, because it costs a lot to establish and maintain a temporary table, so we'd better use an index instead of using a temporary table. For example, the query containing clauses below GROUP BY
is No need to use temp table:
mysql> EXPLAIN SELECT key1, COUNT(*) AS amount FROM s1 GROUP BY key1;
+----+-------------+-------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | index | idx_key1 | idx_key1 | 403 | NULL | 20250 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.01 sec)
From the Extra
prompt Using index
, we can see that the above query only needs to scan idx_key1
the index, and no temporary table is needed anymore.
1.11.12 Start temporary, End temporary
When we talked about subqueries before, we said that the query optimizer will first try to IN
convert subqueries into semi-join
, and semi-join
there are many execution strategies. When the execution strategy DuplicateWeedout
is , it is implemented as the outer query by creating a temporary table When the deduplication operation is performed on the records, Extra
the column of the query execution plan of the driving table will display Start temporary
a prompt, and the column of the query execution plan of the driven table Extra
will display End temporary
a prompt, just like this:
mysql> EXPLAIN SELECT * FROM s1 WHERE key2 IN (SELECT common_field FROM s2 );
+----+-------------+-------+------------+--------+---------------+----------+---------+------------------------+-------+----------+--------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+----------+---------+------------------------+-------+----------+--------------------------------------+
| 1 | SIMPLE | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 100.00 | Using where; Start temporary |
| 1 | SIMPLE | s1 | NULL | eq_ref | idx_key2 | idx_key2 | 5 | testdb.s2.common_field | 1 | 100.00 | Using index condition; End temporary |
+----+-------------+-------+------------+--------+---------------+----------+---------+------------------------+-------+----------+--------------------------------------+
2 rows in set, 1 warning (0.00 sec)
1.11.13 LooseScan
When In
the subquery is converted semi-join
, if the execution strategy is adopted LooseScan
, Extra
the column of the driving table execution plan is to display LooseScan
hints, such as this:
mysql> EXPLAIN SELECT * FROM s1 WHERE key3 IN (SELECT key1 FROM s2 WHERE key1 > 'z');
+----+-------------+-------+------------+-------+---------------+----------+---------+----------------+------+----------+-------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+----------------+------+----------+-------------------------------------+
| 1 | SIMPLE | s2 | NULL | range | idx_key1 | idx_key1 | 403 | NULL | 1 | 100.00 | Using where; Using index; LooseScan |
| 1 | SIMPLE | s1 | NULL | ref | idx_key3 | idx_key3 | 403 | testdb.s2.key1 | 4 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+----------+---------+----------------+------+----------+-------------------------------------+
2 rows in set, 1 warning (0.00 sec)
1.11.14 FirstMatch(tbl_name)
When the In subquery is converted to semi-join
, if the execution strategy is adopted FirstMatch
, the column of the execution plan of the driven table Extra
will display FirstMatch(tbl_name)
hints, such as this:
mysql> explain SELECT * FROM s1 WHERE key1 IN (SELECT common_field FROM s2 WHERE common_field = '638854d');
+----+-------------+-------+------------+------+---------------+----------+---------+-------+-------+----------+------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+-------+----------+------------------------------------------------------------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 403 | const | 1 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 20250 | 10.00 | Using where; FirstMatch(s1); Using join buffer (hash join) |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+-------+----------+------------------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
2. Execution plan in Json format
The statement output we introduced above EXPLAIN
lacks an important attribute to measure the quality of the execution plan - cost. However, MySQL considerately provides us with a way to view the cost of an execution plan: 在EXPLAIN单词和真正的查询语句中间加上FORMAT=JSON
in this way, we can get an json
execution plan in a format that contains the cost of the plan, such as this:
mysql> EXPLAIN FORMAT=JSON SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2 WHERE s1.common_field = 'a'\G
*************************** 1. row ***************************
EXPLAIN: {
"query_block": {
"select_id": 1, # 整个查询语句只有1个SELECT关键字,该关键字对应的id号为1
"cost_info": {
"query_cost": "2758.00" #整个查询的执行成本预计为2758.00
},
"nested_loop": [ #几个表之间采用嵌套循环连接算法执行
# 以下是参与嵌套循环连接算法的各个表的信息
{
"table": {
"table_name": "s1", #s1表是驱动表
"access_type": "ALL", #访问方法为ALL,意味着使用全表扫描访问
"possible_keys": [ #可能使用的索引
"idx_key1"
],
"rows_examined_per_scan": 20250, #查询一次s1表大致需要扫描20250条记录
"rows_produced_per_join": 2025, #驱动表s1的扇出是2025
"filtered": "10.00", #condition filtering代表的百分比
"cost_info": {
"read_cost": "1846.75",#稍后解释
"eval_cost": "202.50",#稍后解释
"prefix_cost": "2049.25", #单次查询s1表总共的成本
"data_read_per_join": "4M" #读取的数据量
},
"used_columns": [ #执行查询中涉及到的列
"id",
"key1",
"key2",
"key3",
"key_part1",
"key_part2",
"key_part3",
"common_field"
],
#对s1表访问时针对单表查询的条件
"attached_condition": "((`testdb`.`s1`.`common_field` = 'a') and (`testdb`.`s1`.`key1` is not null))"
}
},
{
"table": {
"table_name": "s2", #s2表是被驱动表
"access_type": "eq_ref",#访问方法为eq_ref,意味着使用索引等值匹配的方式访问
"possible_keys": [ #可能使用的索引
"idx_key2"
],
"key": "idx_key2", #实际使用的索引
"used_key_parts": [ #使用到的索引列
"key2"
],
"key_length": "5", #key_len
"ref": [ #与key2列进行等值匹配的对象
"testdb.s1.key1"
],
"rows_examined_per_scan": 1,#查询一次s2表大致需要扫描1条记录
"rows_produced_per_join": 2025,#被驱动表s2的扇出是2025(由于后边没有多余的表进行连接,所以这个值也没啥用)
"filtered": "100.00",#condition filtering代表的百分比
#s2表使用索引进行查询的搜索条件
"index_condition": "(cast(`testdb`.`s1`.`key1` as double) = cast(`testdb`.`s2`.`key2` as double))",
"cost_info": {
"read_cost": "506.25", #稍后解释
"eval_cost": "202.50", #稍后解释
"prefix_cost": "2758.00", #查询单次查询s1、多次查询s2表总共的成本
"data_read_per_join": "4M" #读取的数据量
},
"used_columns": [#执行查询中涉及到的列
"id",
"key1",
"key2",
"key3",
"key_part1",
"key_part2",
"key_part3",
"common_field"
]
}
}
]
}
}
1 row in set, 2 warnings (0.01 sec)
We have #
explained EXPLAIN FORMAT=JSON
the output of the statement in the form of a comment followed by it, but you may have questions "cost_info"
about the strange costs in it, how are they calculated? First look at the part of the s1 table "cost_info"
:
"cost_info": {
"read_cost": "1846.75",#稍后解释
"eval_cost": "202.50",#稍后解释
"prefix_cost": "2049.25", #单次查询s1表总共的成本
"data_read_per_join": "4M" #读取的数据量
}
-
read_cost is composed of the following two parts:
-
IO成本
-
test
rows × (1 - filter)
strip recordCPU成本
小提示:
Both rows and filter are the output columns of the execution plan we introduced earlier. In the execution plan in JSON format, rows is equivalent to rows_examined_per_scan, and the name of filtered remains unchanged.
-
-
eval_cost
It is calculated like this:rows × filter
the cost of detecting a record. -
prefix_cost
It is the cost of querying the s1 table alone, that is:read_cost + eval_cost
-
data_read_per_join
Indicates the amount of data that needs to be read in this query, we will not talk about this.
小提示 :
In fact, you don’t need to pay attention to why MySQL uses such a weird way to calculate read_cost and eval_cost, just pay attention to prefix_cost is the cost of querying the s1 table.
The part for s2
the table "cost_info"
is this:
"cost_info": {
"read_cost": "506.25",
"eval_cost": "202.50",
"prefix_cost": "2758.00",
"data_read_per_join": "4M"
}
Since s2
the table is a driven table, it may be read multiple times. The sum here read_cost
is the accumulated value after eval_cost
accessing the table multiple times . You are mainly concerned that the value inside represents the estimated cost of the entire connection query, that is, a single The sum of the query table and the cost after multiple query tables, that is:s2
prefix_cost
s1
s2
506.25+202.50+2049.25=2758.00
三、Extented EXPLAIN
Finally, MySQL also left an easter egg for us. After we use EXPLAIN
the statement to view the execution plan of a query, we can use SHOW WARNINGS
the statement to view some extended information related to the execution plan of the query, such as this:
mysql> EXPLAIN SELECT s1.key1, s2.key1 FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE s2.common_field IS NOT NULL;
+----+-------------+-------+------------+------+---------------+----------+---------+----------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------+-------+----------+-------------+
| 1 | SIMPLE | s2 | NULL | ALL | idx_key1 | NULL | NULL | NULL | 20250 | 90.00 | Using where |
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 403 | testdb.s2.key1 | 79 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------+-------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS\G;
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select `testdb`.`s1`.`key1` AS `key1`,`testdb`.`s2`.`key1` AS `key1` from `testdb`.`s1` join `testdb`.`s2` where ((`testdb`.`s1`.`key1` = `testdb`.`s2`.`key1`) and (`testdb`.`s2`.`common_field` is not null))
1 row in set (0.00 sec)
ERROR:
No query specified
You can see that SHOW WARNINGS
the displayed information has three fields, namely Level
, Code
, Message
. The most common thing we see is Code
for 1003
the information. When Code
the value is 1003
, Message
the information displayed by the field 类似于
is the statement after the query optimizer rewrites our query statement. For example, our above query was originally a left outer join query, but there is a s2.common_field IS NOT NULL
condition, which will cause the query optimizer to optimize the left outer join query into an inner join query. It can also be seen from the fields that the original SHOW WARNINGS
has become up .Message
LEFT JOIN
JOIN
But everyone must pay attention, we say that Message
the information displayed by the field is similar to the statement after the query optimizer rewrites our query statement, not equivalent to that, that is to say, Message
the information displayed by the field is not a standard query statement. In many Under the circumstances, it cannot be run directly in the black frame, it can only be used as a reference to help us understand how MySQL will execute the query statement.
So far, today's study is over, I hope you will become an indestructible self
~~~
You can’t connect the dots looking forward; you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future.You have to trust in something - your gut, destiny, life, karma, whatever. This approach has never let me down, and it has made all the difference in my life
If my content is helpful to you, please 点赞
, 评论
,, 收藏
coding is not easy, everyone's support is the motivation for me to persevere