This article takes you to understand MySQL's Explain execution plan

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. MySQLIt provides us with EXPLAINstatements to help us view the specific execution plan of a certain query statement. The content of this chapter is to help you understand what EXPLAINeach output item of the statement is for, so that we can improve the performance of our query statement in a targeted manner. performance.

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 SELECTthe rest DELETE, INSERT, REPLACEand 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:UPDATEEXPLAINSELECTEXPLAINEXPLAIN

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 s1the 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 s1is the same, and there are records s2in these two tables , and random values ​​are inserted in the other columns except for the id column. 20000In order to let everyone have a better reading experience, we are not going to EXPLAINintroduce 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 MySQLstipulated 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 s1a single-table query of the table, so EXPLAINthere is only one record in the output, and tablethe value of the column is s1, indicating that this record is used to illustrate the s1single-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 tableare s1and respectively s2. These two records are used to explain the access method of s1the table and the table respectively.s2

1.2 id

We know that the query statements we write generally SELECTstart 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';

SELECTThere 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 SELECTkeywords:

SELECT * FROM s1 WHERE key1 IN (SELECT * FROM s2);

Case 2: The case where the query contains UNIONa statement

For example, the following query statement also contains 2 SELECTkeywords:

SELECT * FROM s1  UNION SELECT * FROM s2;

Every time a keyword appears in a query statement SELECT, MySQL assigns it a unique idvalue. This idvalue is EXPLAINthe first column of the statement. For example, there is only one SELECTkeyword in the query below, so there is only one record EXPLAINin the result :id1

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 SELECTafter 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)

s1It can be seen that in the above connection query, the and tables participating in the connection s2correspond to one record respectively, but the corresponding idvalues ​​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 s1the table as the driving table and s2the 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 SELECTkeyword will correspond to a unique idvalue, 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 s1the table is in the outer query, and the outer query has an independent keyword, so the value SELECTof 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 .id1s2SELECTid2

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)

idIt can be seen that although our query statement is a subquery, the value of the third record in the execution plan 2indicates that this record corresponds to a single-table query. From its select_typevalue MATERIALIZED, we can see that the query optimizer is To convert the subquery into a materialized table first. idThen 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 tablethe value of the column of the second record is <subquery2>, indicating that the table is actually idthe corresponding 2subquery The materialized table generated after execution, and then the s1join query will be performed with the materialized table.

For queries containing UNION clauses, it is true that each SELECTkeyword 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 tableLie grows? Don't forget UNIONwhat 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, UNIONthe clause is to combine the query idfor 1and the result set of the query idfor 2and deduplicate, so a <union1, 2>temporary table named (that is, the third record of the execution plan) is created internally table column name), idto 3indicate 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 SELECTkeywords, each SELECTkeyword represents a small query statement, and each SELECTkeyword FROMclause can contain several tables (these tables Used for join query), each table corresponds to a record in the execution plan output, and for SELECTtables in the same keyword, their idvalues ​​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

UNIONQueries that do not contain or in the query statement 子查询are counted as SIMPLEtypes. For example, select_typethe 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 SIMPLEtypes, 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 ALLor , it is composed of several small queries, where the value 子查询of the leftmost query is , for example:select_typePRIMARY

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 s1corresponds to the first record in the execution plan, and its select_typevalue is PRIMARY.

1.3.3 UNION

For a large query containing UNIONor UNION ALL, it is composed of several small queries. Except for the leftmost small query, the select_typevalue 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 UNIONthe deduplication work of the query. The query for the temporary table select_typeis UNION RESULTthe 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-joinform, 该子查询是不相关子查询and the query optimizer decides to execute the subquery by materializing the subquery, the first SELECTkeyword of the subquery represents the The query select_typeis 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_typeis PRIMARY, and the subquery select_typeis SUBQUERY. It should be noted that because select_typethe SUBQUERYsubquery 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-joinSELECTselect_typeDEPENDENT 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_typethe DEPENDENT SUBQUERYquery may be executed multiple times.

1.3.7 DEPENDENT UNION

In a large query containing UNIONor UNION ALL, if each small query depends on the outer query, except for the leftmost small query, the select_typevalue 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 UNIONconnected 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_typeis DEPENDENT SUBQUERY, and SELECT key1 FROM s1 WHERE key1 ='b'this query select_typeisDEPENDENT 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_typeis 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 idfor 2represents the execution method of the subquery, and it select_typemeans DERIVEDthat the subquery is executed in a materialized manner. idThe record for 1represents 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_typeWhen 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)

idThe value of the third record in the execution plan 2indicates that this record corresponds to a single-table query. From its select_typevalue MATERIALIZED, it can be seen that the query optimizer needs to convert the subquery first 物化表. idThen 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 tablethe value of the column of the second record is <subquery2>, indicating that the table is actually idthe corresponding 2subquery The materialized table generated after execution, and then the s1join 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 partitionsvalues ​​​​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 typethat the value of the column is ref, indicating that the access method MySQLis 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:refs1InnoDBsystemconsteq_refreffulltextref_or_nullindex_mergeunique_subqueryindex_subqueryrangeindexAL

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 MyISAMtable 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 typethat 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 constexample:

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 s1the driving table as s2the driven table, and the key s2access method is to eq_refindicate that the s2table 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 NULLa 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 Intersectionthese Unionthree Sort-Unionindex 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 typethe 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 s1the query on the table.

1.5.7 unique_subquery

eq_refSimilar to the access method of the driven table in the two-table join , unique_subqueryit is aimed at some query statements containing IN subqueries, if the query optimizer decides to convert the INsubquery to EXISTSa subquery, and the subquery can use the primary key for equivalent matching If , then typethe 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 typeis , indicating that the index of the column unique_subquerywill be used when executing the subquery .id

1.5.8 index_subquery

index_subquerySimilar 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_part3one column in the search condition, and these two columns happen to be included in idx_key_partthis index, but the search condition key_part3cannot directly use the index for ref or rangemethod access, and can only scan The records of the entire idx_key_partindex, so typethe 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 Allthis access method, all other access methods can use the index, and except for index_mergethe access method, the rest of the access methods can only use at most one index.

1.6 possible_keys和key

In EXPLAINthe execution plan output by the statement, possible_keysthe columns indicate which indexes may be used when executing a single-table query on a certain table in a certain query statement, and the keycolumns 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_keysThe value of the column in the above execution plan is idx_key1, idx_key3which means that the query may use two indexes idx_key1, idx_key3and keythe 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_key3to 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_keysthe column is empty, but keythe 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_keysthe 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_lenThe 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 is utf8, then the maximum storage space actually occupied by the column is 100 × 3 = 300bytes.
  • If the index column can store NULLa value, it is 1 byte more key_lenthan 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 idthe column's type is INTand cannot store values, the size is NULLwhen using the column's index . When indexed columns can store values, such as:key_len4NULL

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_lenthat the column becomes more 5than when using the column index . For variable-length index columns, such as the following query:id1

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 400bytes, and because this column allows storage of NULLvalues, it key_lenneeds to be added 1, and because this column is 可变长度列, it key_lenneeds 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 serverkey_lenidx_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_lenWe can see the value from the column of the execution plan , which means that MySQL can only use one index column of the index 403in 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_lenThe value of the column in the execution plan of this query is , indicating that the two index columns of 806the 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_subqueryref

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 refthat the value of the column is const, indicating that when using idx_key1the index to execute the query, key1the 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)

s2It can be seen that the access method to the driven table is , and the value of the eq_refcorresponding 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:reftestdb.s1.idPRIMARYs2idtestdb.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 s2on 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.refreffuncs2key1

1.9 rows

If the query optimizer decides to use a full table scan to execute a query on a table, the rowscolumn of the execution plan represents the expected number of rows to be scanned. If an index is used to execute the query, the rowscolumn 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)

rowsWe 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_key1satisfies the condition ' after analyzing the cost of using the query .key1 > 'z1

1.10 filtered

When analyzing the cost of connection queries, a condition filteringconcept 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 |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+

keyIt can be seen from the columns of the execution plan that the query uses idx_key1the index to execute the query, and it can be seen from rowsthe columns that there are satisfied key1 > 'z'records 1. The column of the execution plan filteredrepresents the query optimizer predicts 1how many records in this record meet the rest of the search conditions, that is, common_field = 'aathe percentage of this condition. filteredThe 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, filteredthe value of this column is meaningless. We pay more attention to filteredthe 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 s1as a driving table and s2as a driven table. We can see that the execution plan of the driving s1table rowsis 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 tablefiltered10.0020250× 10.00% = 20252025

1.11 Extra

As the name suggests, Extracolumns 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

FROMThis 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 MINan or MAXaggregate function in the query list, but there are no WHERErecords 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, Extrathe additional information will be prompted in the column. For example, the following query only needs to be used idx_key1and 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_key1the 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 rangethe execution of the access method, this condition only involves key1columns after all, so MySQL has improved the above steps:

  • First, according to key1 > 'z'this condition, locate idx_key1the 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 WHEREthere are search conditions for the table in the clauses of the statement, Extrathe 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 WHEREclauses of the statement have other search conditions than the columns included in the index, Extrathe above additional information will also be prompted in the column. For example, although the query below uses idx_key1the index to execute the query, the search conditions include not only key1the search conditions key1 = 'a', but also common_fieldthe search conditions, so Extrathe column will display Using wherethe 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 buffermemory 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)

s2There 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 because s2the access to the table cannot effectively use the index, so we have to settle for the next best thing and use it join bufferto reduce the number of accesses to the s2 table, thereby improving performance.
  • Using where: You can see that there is a s1.common_field = s2.common_fieldcondition in the query statement, because s1it is the driving table and s2the driven table, so when s2the table is accessed, s1.common_fieldthe value of the value has been determined, so in fact s2the condition of the query table is , so additional information s2.common_field = 一个常数is prompted .Using where

1.11.7 Not exists

When we use the left outer join, if the WHERE clause contains a NULLsearch 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 NULLin the execution plan of the table will be ExtraPrompt Not existsadditional 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, s2the table is the driven table, s2.idthe columns are not allowed to store NULLvalues, and the search conditions WHEREcontained in the clauses s2.id IS NULLmean that the records that must be the driving table cannot be matched in the driven table ONThe 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, ONthen 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(…)

ExtraIf 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 Unionexecuted by index merging; Using sort_union(...), indicating that Sort-Unionthe 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_key3with idx_key1these two indexes Unionto 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 LIMITclause 0is , 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_key1the index to directly fetch the records key1of 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 DISTINCTand sorting GROUP BY. UNIONTo 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 Extrawill 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 BYis 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 Extraprompt Using index, we can see that the above query only needs to scan idx_key1the 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 INconvert subqueries into semi-join, and semi-jointhere are many execution strategies. When the execution strategy DuplicateWeedoutis , it is implemented as the outer query by creating a temporary table When the deduplication operation is performed on the records, Extrathe column of the query execution plan of the driving table will display Start temporarya prompt, and the column of the query execution plan of the driven table Extrawill display End temporarya 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 Inthe subquery is converted semi-join, if the execution strategy is adopted LooseScan, Extrathe column of the driving table execution plan is to display LooseScanhints, 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 Extrawill 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 EXPLAINlacks 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=JSONin this way, we can get an jsonexecution 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=JSONthe 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_costIt is calculated like this: rows × filterthe cost of detecting a record.

  • prefix_costIt is the cost of querying the s1 table alone, that is:read_cost + eval_cost

  • data_read_per_joinIndicates 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 s2the 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 s2the table is a driven table, it may be read multiple times. The sum here read_costis the accumulated value after eval_costaccessing 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:s2prefix_costs1s2506.25+202.50+2049.25=2758.00

三、Extented EXPLAIN

Finally, MySQL also left an easter egg for us. After we use EXPLAINthe statement to view the execution plan of a query, we can use SHOW WARNINGSthe 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 WARNINGSthe displayed information has three fields, namely Level, Code, Message. The most common thing we see is Codefor 1003the information. When Codethe value is 1003, Messagethe 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 NULLcondition, 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 WARNINGShas become up .MessageLEFT JOINJOIN

But everyone must pay attention, we say that Messagethe 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, Messagethe 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

insert image description here

Guess you like

Origin blog.csdn.net/liang921119/article/details/130844376