MySQL优化之EXPLAN执行计划

** 备注 **

本文改编自https://www.processon.com/view/5d4fe8f4e4b04399f5a0303e?fromnew=1#map

待阅读完千金良方MySQL后对此文进行二次优化

本次案例使用到的SQL

--建立下面格式的表S1,S2结构

CREATE TABLE single_table (

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)

) Engine=InnoDB CHARSET=utf8;

--说明:

--主键id列

--二级索引列 key1,key2(唯一二级),key3

--联合索引列 (keypart1, keypart2, keypart3))

--普通列commonfield

--使用存储过程插入10000条语句

CREATE DEFINER=`root`@`localhost` PROCEDURE `InsertRecordS1`()

BEGIN

DECLARE count int default 1;

WHILE count<=10000 DO

insert into s1 (id,key1,key2,key3,key_part1,key_part2,key_part3,common_field) values (null,CONCAT('key1',count),count,CONCAT('key3',count),CONCAT('key_part1',count),CONCAT('key_part2',count),CONCAT('key_part3',count),CONCAT('common',count));

set count=count+1;

END WHILE;

END

call InsertRecordS1();

实际用到的SQL

```sql

--创建测试用表s1和s2;

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)

) Engine=InnoDB CHARSET=utf8;

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)

) Engine=InnoDB CHARSET=utf8;

--使用存储过程插入10000条语句

delimiter //

CREATE DEFINER=`root`@`localhost` PROCEDURE `InsertRecordS1`()

BEGIN

DECLARE count int default 1;

WHILE count<=10000 DO

insert into s1 (id,key1,key2,key3,key_part1,key_part2,key_part3,common_field) values (null,CONCAT('key1',count),count,CONCAT('key3',count),CONCAT('key_part1',count),CONCAT('key_part2',count),CONCAT('key_part3',count),CONCAT('common',count));

set count=count+1;

END WHILE;

END

//

delimiter ;

call InsertRecordS1();

delimiter //

CREATE DEFINER=`root`@`localhost` PROCEDURE `InsertRecordS2`()

BEGIN

DECLARE count int default 1;

WHILE count<=10000 DO

insert into s2 (id,key1,key2,key3,key_part1,key_part2,key_part3,common_field) values (null,CONCAT('key1',count),count,CONCAT('key3',count),CONCAT('key_part1',count),CONCAT('key_part2',count),CONCAT('key_part3',count),CONCAT('common',count));

set count=count+1;

END WHILE;

END

//

delimiter ;

call InsertRecordS2();

使用方式

在查询语句前面加上EXPLAIN关键字

EXPLAIN SELECT * FROM tablename;

各字段含义

名称

解释

id

在一个大的查询语句中每个SELECT关键字都对应一个唯一的ID

select_type

SELECT关键字对应的那个查询的类型

type

单表访问的方法

table

表名

partitions

匹配的分区信息(暂时不涉及)

possible_keys

可能用到的索引

key

实际上使用的索引

key_len

实际使用到的索引长度

ref

当使用索引列等值查询时,与索引列进行等值匹配的对象信息 比如常量const,函数func,或者是驱动表的主键

rows

预估的需要读取的记录数

filtered

某个表经过搜索条件过滤后剩余记录条数的百分比 (扇出率)

Extra

一些额外信息

id列详解

查询语句中每出现一个SELECT关键字,MySQL就会为它分配一个唯一的ID值

注意点

对于连接查询来说,一个SELECT关键字后边的FROM子句中可以跟随多个表, 所以在连接查询的执行计划中,每个表都会对应一条记录,但是这些记录的id值都是相同的

在连接查询的执行计划中,每个表都会对应一条记录,这些记录的id列的值是相同的, 出现在前边的表表示驱动表,出现在后边的表表示被驱动表。

查询优化器可能对涉及子查询的查询语句进行重写,从而转换为连接查询。 那么这时候他们的ID值就一样了啦

对于UNION查询产生的临时表的表示

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 | 9923 | 100.00 | NULL |

| 2 | UNION | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 9903 | 100.00 | NULL |

| NULL | UNION RESULT | | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |

+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+

3 rows in set, 1 warning (0.00 sec)

备注

MySQL使用的是内部的临时表。正如上边的查询计划中所示,UNION子句是为了把id为1的查询和id为2的查询的结果集合并起来并去重,所以在内部创建了一个名为的临时表(就是执行计划第三条记录的table列的名称),id为NULL表明这个临时表是为了合并两个查询的结果集而创建的。

select_type列详解

取值范围

名称

解释

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 outer query

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)

各取值范围详解

SIMPLE

查询语句中不包含UNION或者子查询的查询

当然连接查询也算是SIMPLE类型

PRIMARY

对于包含UNION、UNION ALL或者子查询的大查询来说,它是由几个小查询组成的, 其中最左边的那个查询的select_type值就是PRIMARY

英文解释直译过来也是 最外层的查询

UNION

对于包含UNION或者UNION ALL的大查询来说,它是由几个小查询组成的, 其中除了最左边的那个小查询以外,其余的小查询的select_type值就是UNION

UNION_RESULT

MySQL选择使用临时表来完成UNION查询的去重工作, 针对该临时表的查询的select_type就是UNION RESULT

SUBQUERY

如果包含子查询的查询语句不能够转为对应的semi-join的形式,并且该子查询是不相关子查询,并且查询优化器决定采用将该子查询物化的方案来执行该子查询时,该子查询的第一个SELECT关键字代表的那个查询的select_type就是SUBQUERY

由于select_type为SUBQUERY的子查询由于会被物化,所以只需要执行一遍

DEPENDENT SUBQUERY

如果包含子查询的查询语句不能够转为对应的semi-join的形式,并且该子查询是相关子查询, 则该子查询的第一个SELECT关键字代表的那个查询的select_type就是DEPENDENT SUBQUERY

select_type为DEPENDENT SUBQUERY的查询可能会被执行多次。

DEPENDENT UNION

在包含UNION或者UNION ALL的大查询中,如果各个小查询都依赖于外层查询的话, 那除了最左边的那个小查询之外,其余的小查询的select_type的值就是DEPENDENT UNION。

DERIVED

对于采用物化的方式执行的包含派生表的查询,该派生表对应的子查询的select_type就是DERIVED

MATERIALIZED

当查询优化器在执行包含子查询的语句时,选择将子查询物化之后与外层查询进行连接查询时, 该子查询对应的select_type属性就是MATERIALIZED

UNCACHEABLE SUBQUERY

并不常用

UNCACHEABLE UNION

并不常用

table列详解

查询哪个表

partitions列详解

暂时用不上

type列详解

取值范围

system

当表中只有一条记录并且该表使用的存储引擎的统计数据是精确的,比如MyISAM、Memory,那么对该表的访问方法就是system。

当表中只有一条记录并且该表使用的存储引擎的统计数据是不精确的,比如InnoDB,那么对该表的访问方法就是全表扫描。

const

根据主键或者唯一二级索引列与常数进行等值匹配时,对单表的访问方法就是const

eq_ref

在连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的(如果该主键或者唯一二级索引是联合索引的话,所有的索引列都必须进行等值比较),则对该被驱动表的访问方法就是eq_ref

ref

当通过普通的二级索引列与常量进行等值匹配时来查询某个表,那么对该表的访问方法就可能是ref

ref_or_null

当对普通二级索引进行等值匹配查询,该索引列的值也可以是NULL值时,那么对该表的访问方法就可能是ref_or_null

index_merge

使用Intersection、Union、Sort-Union这三种索引合并的方式来执行查询

unique_subquery

类似于两表连接中被驱动表的eq_ref访问方法,unique_subquery是针对在一些包含IN子查询的查询语句中,如果查询优化器决定将IN子查询转换为EXISTS子查询,而且子查询可以使用到主键进行等值匹配的话,那么该子查询执行计划的type列的值就是unique_subquery

index_subquery

index_subquery与unique_subquery类似,只不过访问子查询中的表时使用的是普通的索引,比如这样:

range

如果使用索引获取某些范围区间的记录,那么就可能使用到range访问方法

index

当我们可以使用索引覆盖,但需要扫描全部的索引记录时,该表的访问方法就是index

ALL

熟悉的全表扫描

fulltext

全文索引,支持一般,专业人干专业事,建议上ES或者Solr

possible_keys与key列详解

possible_keys列表示在某个查询语句中,对某个表执行单表查询时可能用到的索引有哪些,key列表示实际用到的索引有哪些

key_len列详解

key_len列表示当优化器决定使用某个索引执行查询时,该索引记录的最大长度

作用

在使用联合索引的时候就可以知道用了哪几列啦

计算规则

对于使用固定长度类型的索引列来说,它实际占用的存储空间的最大长度就是该固定值,对于指定字符集的变长类型的索引列来说,比如某个索引列的类型是VARCHAR(100),使用的字符集是utf8,那么该列实际占用的最大存储空间就是100 × 3 = 300个字节。

如果该索引列可以存储NULL值,则key_len比不可以存储NULL值时多1个字节。

对于变长字段来说,都会有2个字节的空间来存储该变长列的实际长度。

举一个栗子

ref列详解

ref列展示的就是与索引列作等值匹配的东东是个啥,比如只是一个常数或者是某个列。

常数const的栗子

某一列的栗子

函数func的栗子

rows列详解

如果查询优化器决定使用全表扫描的方式对某个表执行查询时,执行计划的rows列就代表预计需要扫描的行数, 如果使用索引来执行查询时,执行计划的rows列就代表预计扫描的索引记录行数。

filtered列详解

驱动表扇出的比例,当然是越低越好啦

Extra列详解

常见取值

No tables used

当查询语句的没有FROM子句时将会提示该额外信息

Impossible WHERE

查询语句的WHERE子句永远为FALSE时将会提示该额外信息

No matching min/max row

当查询列表处有MIN或者MAX聚集函数,但是并没有符合WHERE子句中的搜索条件的记录时,将会提示该额外信息

Using index

当我们的查询列表以及搜索条件中只包含属于某个索引的列,也就是在可以使用索引覆盖(bu)的情况下,在Extra列将会提示该额外信息

Using index condition

如果在查询语句的执行过程中将要使用索引条件下推这个特性,在Extra列中将会显示Using index condition

什么是索引下推

SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%a';

对于上面的这条SQL语句,因为这时候走的是以range访问方式走的key1索引。 而key1 LIKE '%a'由于不满足前缀匹配,所以需要回表访问的时候再过滤

可是这个条件列又刚好是key1索引列。如果在二级索引的时候,就进行过滤条件判断,不放到回表后判断。 那么就可以减少回表访问的次数,也就是减少随机IO的发生。

所以把【如果在二级索引的时候,就进行过滤条件判断,不放到回表后判断 】这种情况叫做索引下推

Using where

当我们使用全表扫描来执行对某个表的查询,并且该语句的WHERE子句中有针对该表的搜索条件时

当使用索引访问来执行对某个表的查询,并且该语句的WHERE子句中有除了该索引包含的列之外的其他搜索条件时,在Extra列中也会提示上述额外信息。

Using join buffer (Block Nested Loop)

使用JOIN BUFFER来降低被驱动表的访问次数从而提高查询效率

Not exists

当我们使用左(外)连接时,如果WHERE子句中包含要求被驱动表的某个列等于NULL值的搜索条件,而且那个列又是不允许存储NULL值的,那么在该表的执行计划的Extra列就会提示Not exists额外信息. (也就是如果在被驱动表中匹配到一条记录,那么就不再往下走了)

Using intersect(...)、Using union(...)和Using sort_union(...)

使用到索引合并的时候就会有这种情况啦

Zero limit

当我们的LIMIT子句的参数为0时

Using filesort

不根据所以树进行排序,而在内存中或者磁盘上进行排序的方式统称为文件排序(英文名:filesort)

Using temporary

在许多查询的执行过程中,MySQL可能会借助临时表来完成一些功能,比如去重、排序之类的,比如我们在执行许多包含DISTINCT、GROUP BY、UNION等子句的查询过程中,如果不能有效利用索引来完成查询,MySQL很有可能寻求通过建立内部的临时表来执行查询。

DISTINCT的栗子

Group by 的栗子

Start temporary, End temporary

查询优化器会优先尝试将IN子查询转换成semi-join,而semi-join又有好多种执行策略,当执行策略为DuplicateWeedout时,也就是通过建立临时表来实现为外层查询中的记录进行去重操作时,驱动表查询执行计划的Extra列将显示Start temporary提示,被驱动表查询执行计划的Extra列将显示End temporary提示

LooseScan

在将In子查询转为semi-join时,如果采用的是LooseScan执行策略,则在驱动表执行计划的Extra列就是显示LooseScan提示

FirstMatch(tbl_name)

在将In子查询转为semi-join时,如果采用的是FirstMatch执行策略,则在被驱动表执行计划的Extra列就是显示FirstMatch(tbl_name)提示

Json格式的执行计划

作用

查看某个执行计划花费的成本

用法

在EXPLAIN单词和真正的查询语句中间加上FORMAT=JSON。

EXPLAIN FORMAT=JSON SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2 WHERE s1.common_field = 'a'

举一个栗子

Extented EXPLAIN

作用

比如可以看到 类似于查询优化器将我们的查询语句重写后的语句

使用

在执行计划执行后, 执行SHOW WARNINGS

mysql执行计划重用_MySQL 优化之EXPLAN执行计划相关推荐

  1. mysql字段简索引_MySQL优化看这一篇就够了

    本文概要 概述 为什么要优化 系统的吞吐量瓶颈往往出现在数据库的访问速度上 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢 数据是存放在磁盘上的,读写速度无法和内存相比 如何优化 ...

  2. mysql执行sql流程_MySQL架构与SQL执行流程

    MySQL架构设计 下面是一张MySQL的架构图: 上方各个组件的含义如下: Connectors 指的是不同语言中与SQL的交互 Management Serveices & Utiliti ...

  3. mysql定时器每月执行一次_MySQL定时器可以设置执行次数吗

    满意答案 jgiewqh57 2018.01.26 采纳率:41%    等级:13 已帮助:10318人 mysql要实现定时执行sql语句就要用到Event 具体操作如下: 先看看看event 事 ...

  4. mysql 执行效率命令_MySQL优化--explain 分析sql语句执行效率

    MySQL优化--explain 分析sql语句执行效率 explain 命令 explain 命令在解决数据库性能上市第一推荐使用命令,大部分的性能问题可以通过此命令来简单解决,explain可以用 ...

  5. mysql防止索引崩溃_MySQL优化之避免索引失效的方法

    在上一篇文章中,通过分析执行计划的字段说明,大体说了一下索引优化过程中的一些注意点,那么如何才能避免索引失效呢?本篇文章将来讨论这个问题. 避免索引失效的常见方法 1.对于复合索引的使用,应按照索引建 ...

  6. mysql+index组合索引_MySQL 优化之 index merge(索引合并)

    标签: MySQL5.0之前,一条语句中一个表只能使用一个索引,无法同时使用多个索引.但是从5.1开始,引入了 index merge 优化技术,对同一个表可以使用多个索引.理解了 index mer ...

  7. mysql 查询慢 分析_MySQL优化:定位慢查询的两种方法以及使用explain分析SQL

    一条SQL查询语句在经过MySQL查询优化器处理后会生成一个所谓的执行计划,这个执行计划展示了具体执行查询的方式,比如多表连接的顺序是什么,对于每个表采用什么访问方法来具体执行查询等等. 本章的内容就 ...

  8. mysql 回表查询优化_MySQL优化:如何避免回表查询?什么是索引覆盖?

    转自:https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651962609&idx=1&sn=46e59691257 ...

  9. mysql 批量加索引_mysql优化:按期删数据 + 批量insert + 字符串加索引为何很傻

    嗯,犯了一个很低级的错误,最近暴露出来了.html 背景:mysql 1. 内部平台,接口间断性无返回,查询日志注意到失败时,接口耗时达到4000+(正常状态:100+ms)git 2. 增长日志打点 ...

最新文章

  1. PHP连接数据库的三种方式
  2. 局域网访问php forbidden,PHP访问时Forbidden403错误
  3. Java拆分字符串的函数——split
  4. 序列、触发器和存储过程三者之间有啥关系
  5. 如何查看AWS实例上使用的key value pair名称
  6. vim 底行命令模式下的全局命令 g(global)
  7. (三)2005年我的第一次软件行业创业,烧掉30万、2年时间打水漂的惨痛教训
  8. python在input输入数字为何是str_Python基础笔记:input()输入与数据类型转换
  9. LTE网络架构及鉴权
  10. Qos拥塞避免原理与实验
  11. Windows 11 配置JDK 环境变量
  12. 马克维茨组合matlab,马克维茨投资组合模型的matlab计算.pdf
  13. 面试被问到【未来3-5年的职业规划】,到底该怎么回答?
  14. Adaptive icon 二 设计自适应图标
  15. 《关键对话》:高效沟通与数据分析的意义不言而喻
  16. 基本概念:节点、集群、分片及副本
  17. 歌礼与先声签订利托那韦片供应协议;索迪斯华东物流新仓于上海松江区开业 | 美通企业日报...
  18. 读查理·芒格---《穷查理宝典》
  19. 面试技巧: 轻松过关10种方法
  20. pyecharts官网

热门文章

  1. 华为云:数字化时代的“成长引擎”播---百度新闻|智谷趋势
  2. python实现r树存储地理位置_R树--理解平面思维
  3. ATX+Python+uiautomator2环境下进行手机UI自动化测试
  4. PDF文件如何删除部分页面?
  5. 服务器Reboot以后无法重启的一个原因
  6. APP设计师拿到APP产品原型开始,七步搞定APP设计
  7. PythonAI对话机器人
  8. mails plugin
  9. 国外问卷调查是不是骗局的,合法吗?
  10. 配置无线用户接入WLAN实验