一、背景
在日常工作中,可能会收到一些超时或慢响应的告警,最根到底可能是因为一些执行时间比较的SQL语句,这就跟我们平时开发需要注意细节相关了。那么找到这些SQL语句怎么优化呢?到底是哪里的问题导致SQL执行时间长呢? 这个时候Explain命令尤其重要,它可以查看该SQL语句有没有使用上索引、使用了哪个索引、有没有做全表扫描、有没有使用临时表等等。下面都是基于mysql 8进行案例说明的。二、语法
EXPLAIN语句提供有关MySQL如何执行语句的信息。 EXPLAIN 通常与SELECT,DELETE,INSERT,REPLACE和UPDATE语句一起使用。 例如:explain select * from tb_student;三、explain 输出列详解
Column | JSON Name | Meaning |
id | select_id | The SELECT identifier |
select_type | None | The SELECT type |
table | table_name | The table for the output row |
partitions | partitions | The matching partitions |
type | access_type | The join type |
possible_keys | possible_keys | The possible indexes to choose |
key | key | The index actually chosen |
key_len | key_length | The length of the chosen key |
ref | ref | The columns compared to the index |
rows | rows | Estimate of rows to be examined |
filtered | filtered | Percentage of rows filtered by table condition |
Extra | None | Additional information |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+ | 1 | SIMPLE | t | NULL | index | NULL | idx_id | 5 | NULL | 1 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
-
id:select标志符,有几个 select 就有几个id,并且id的顺序是按 select 出现的顺序增长的。MySQL将 select 查询分为简单查询和复杂查询。复杂查询可以如下:
mysql> explain select (select 1 from t limit 1) from t1; +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | PRIMARY | t1 | NULL | index | NULL | PRIMARY | 4 | NULL | 1 | 100.00 | Using index | | 2 | SUBQUERY | t | NULL | index | NULL | idx_id | 5 | NULL | 1 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
- select_type
-
- simple:简单查询。查询不包含子查询和union
mysql> explain select * from t; +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+ | 1 | SIMPLE | t | NULL | index | NULL | idx_id | 5 | NULL | 1 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
- simple:简单查询。查询不包含子查询和union
-
- primary:复杂查询中最外层的 select。如上面一个复杂查询
- union:在 union 中的第二个或随后的 select
mysql> explain select id from t1 union select id from t2; +----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+ | 1 | PRIMARY | t1 | NULL | index | NULL | PRIMARY | 4 | NULL | 1 | 100.00 | Using index | | 2 | UNION | t2 | NULL | index | NULL | PRIMARY | 4 | NULL | 2 | 100.00 | Using index | | NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary | +----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+
-
- DEPENDENT UNION UNION中的第二个或后面的SELECT语句,取决于外面的查询
mysql> explain select * from t where id in (select t1.id from t1 union select id from t2 ); +----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+--------------------------+ | 1 | PRIMARY | t | NULL | index | NULL | idx_id | 5 | NULL | 1 | 100.00 | Using where; Using index | | 2 | DEPENDENT SUBQUERY | t1 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | func | 1 | 100.00 | Using index | | 3 | DEPENDENT UNION | t2 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | func | 1 | 100.00 | Using index | | NULL | UNION RESULT | <union2,3> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary | +----+--------------------+------------+------------+--------+---------------+---------+---------+------+------+----------+--------------------------+
- DEPENDENT UNION UNION中的第二个或后面的SELECT语句,取决于外面的查询
-
- UNION RESULT union的结果,如上。
- table
- partitions
- type
- system 该表只有一行(=系统表)。 这是const join类型的特例
-
const 该表最多具有一个匹配行,该行在查询开始时读取。 因为只有一行,所以优化器的其余部分可以将这一行中列的值视为常量。 const表非常快,因为它们只能读取一次。当将PRIMARY KEY或UNIQUE索引的所有部分与常量值进行比较时,将使用const。形如:
SELECT * FROM tbl_name WHERE primary_key=1; SELECT * FROM tbl_name WHERE primary_key_part1=1 AND primary_key_part2=2;
example:
mysql> explain select * from t1 where id = 1; +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | 1 | SIMPLE | t1 | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
- eq_ref
SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column; SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1;
example:
mysql> explain select * from t1, t2 where t1.id = t2.id; +----+-------------+-------+------------+--------+---------------+---------+---------+---------------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------+---------+---------+---------------+------+----------+-------+ | 1 | SIMPLE | t1 | NULL | ALL | PRIMARY | NULL | NULL | NULL | 1 | 100.00 | NULL | | 1 | SIMPLE | t2 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | test_db.t1.id | 1 | 100.00 | NULL | +----+-------------+-------+------------+--------+---------------+---------+---------+---------------+------+----------+-------+
- ref
SELECT * FROM ref_table WHERE key_column=expr; SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column; SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1;
example:
mysql> explain select * from t where id = 1; +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | t | NULL | ref | idx_id | idx_id | 5 | const | 1 | 100.00 | Using index | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+
- fulltext 使用FULLTEXT索引执行连接。
- ref_or_null
SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;
- index_merge
SELECT * FROM tbl_name WHERE key1 = 10 OR key2 = 20;
- unique_subquery
value IN (SELECT primary_key FROM single_table WHERE some_expr)
- index_subquery
value IN (SELECT key_column FROM single_table WHERE some_expr)
- range
mysql> explain select * from t1 where t1.id > 1; +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | SIMPLE | t1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 1 | 100.00 | Using where | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.01 sec)
- index
mysql> explain select id from t1; +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | SIMPLE | t1 | NULL | index | NULL | PRIMARY | 4 | NULL | 1 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
- all
- possible_keys
- key
- key_len
- ref
- rows
- filtered
- extra
-
- Distinct
-
- Not exists
-
- Using filesort
-
- Using index
-
- Using temporary
-
- Using where
四、参考文献
官方解释:https://dev.mysql.com/doc/refman/8.0/en/explain-output.html表结构:
mysql> show create table t; +-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | t | CREATE TABLE `t` ( `id` int(11) DEFAULT NULL, `name` varchar(10) NOT NULL, PRIMARY KEY (`name`), KEY `idx_id` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci | +-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> show create table t1; +-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+ | t1 | CREATE TABLE `t1` ( `id` int(11) NOT NULL, `age` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci | +-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> show create table t2; +-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+ | t2 | CREATE TABLE `t2` ( `id` int(11) NOT NULL, `sex` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci | +-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)