文章目录

  • 看完本篇文章你能学到什么?
  • 一、MySQL执行计划
    • 1.1 id字段
    • 1.2 select_type 字段
    • 1.3 table 字段
    • 1.4 partitions 字段
    • 1.5 type字段
    • 1.6 possible_keys 字段
    • 1.7 key 字段
    • 1.8 key_len 字段
    • 1.9 ref 字段
    • 1.10 rows 字段
    • 1.11 filtered 字段
    • 1.12 extra 字段
  • 二、总结
    • 好了,本篇就说到这里了,看完觉得有帮助的童鞋记得点赞!点赞!点赞!(重要的事情说三遍)

看完本篇文章你能学到什么?

1、掌握MySQL执行计划所有字段含义以及内容,为后续SQL调优提供帮助

2、对SQL调优有初步认识

一、MySQL执行计划

项目开发中,性能往往都是是我们重点关注的问题,其实很多时候一个SQL往往是整个请求中瓶颈最大的地方,因此我们必须了解SQL语句的执行过程、数据库中是如何扫描表、如何使用索引的、是否命中索引等信息来帮助我们做SQL语句的优化。MySQL提供了explain/desc语句,来显示这条SQL语句的执行计划,执行计划可以帮助我们查看SQL语句的执行情况,我们可以根据反馈的结果来进行SQL的优化。

  • 准备测试数据
use test;
CREATE TABLE `test`.`role`  (`id` int(11) NOT NULL,`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;CREATE TABLE `test`.`user`  (`id` int(11) NOT NULL,`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`role_id` int(11) NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

explaindesc效果一样,都是帮我们列出SQL语句的执行计划。

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

两个语句的效果是一模一样的,我们后面就统一使用explain来查看SQL语句的执行计划。

1.1 id字段

id字段存在三种情况:

1)id相同:id越靠前的表越先执行

explain select * from user u left join role r on u.role_id=r.id;


2)id不同:id越大的表越先执行

explain select * from user u where u.role_id=(select id from role r where r.id=1);


3)id有相同,也有不同:id相同越大的表越先执行,在id相同的表中,id越靠前的表越先执行

1.2 select_type 字段

  • SIMPLE:简单的 select 查询,查询中不包含子查询或者 union
explain select * from user;

  • PRIMARY:查询条件中包含有子查询时最外层的表(u1)
explain select * from user u1 where u1.id =(select id from user u2 where u2.id=1);

  • UNION(u2):使用到union关联时,union关联的表
  • UNION RESULT(<union1,2>):使用union时,最终的结果集表
explain select * from user u1 union select * from user u2;

  • DEPENDENT UNION(u3,u4):在子查询中使用到union的第二个以上的表
  • DEPENDENT SUBQUERY(u2):在子查询中,使用到union的第一表
EXPLAIN SELECT*
FROMuser u1
WHEREu1.id IN ( SELECT id FROM user u2 WHERE u2.id = 1 UNION SELECT id FROM user u3 WHERE u3.id = 2 UNION SELECT id FROM user u4 WHERE u4.id = 2  );

  • SUBQUERY(u2):条件子查询中的表
explain select * from user u1 where u1.id =(select id from user u2 where u2.id=1);

  • SUBQUERY(u2,u3):条件中的子查询中的表(包括多重层级)
explain select * from user u1
where u1.name =(select name from user u2 where u2.name=(select name from user u3 where u3.name='zs')
);

  • DEPENDENT SUBQUERY(r1): 子查询中的条件依赖于外部的查询(r1的条件是u1表中的数据)
explain select * from user u1 where u1.role_id=(select id from role r1 where u1.id=1);

  • DERIVED(u1):衍生表的from子表(该子表必须使用union关联其他表)
explain select * from
(select * from user u1 where u1.role_id=1 union select * from user u2 where u2.name='zs') temp;

1.3 table 字段

表示该SQL语句是作用于那张表的,取值为:表名、表别名、衍生表名等。

explain select * from user;explain select * from user u1;

1.4 partitions 字段

涉及到分区的表

  • 准备数据
create table goods_partitions (id int auto_increment, name varchar(12),primary key(id)
)
partition by range(id)(partition p0 values less than(10000),partition p1 values less than MAXVALUE
);
  • 查看MySQL的物理存储路径:
show variables like '%dir%';


查看物理存储文件,发现多了不同的文件来存储

  • 查看查询语句所使用到的分区:
explain select * from goods_partitions;

整个goods_partitions使用到了两个分区。

  • 查询id<1000的记录(属于p0分区)
explain select * from goods_partitions where id<1000;

1.5 type字段

反应一段SQL语句性能指标的重要参数,可以通过此参数来判断是否使用到了索引、是否全表扫描、是否范围查询等。

插入测试数据:

insert into role values(1,'保洁');
insert into role values(2,'保安');
insert into role values(3,'厨师');
insert into user values(1,'zs',1);
  • null:代表不访问任何表
explain select 1;

  • system:表中只有一条记录,并且此表为系统表(一般很少出现)
use mysql;           -- 切换到mysql数据库
explain select * from db where host='localhost';

  • const:通过唯一索引查询到的数据,只查询一次就查询到了
explain select * from user where id=1;
explain select * from user where name='zs';

分别根据name和id查询,发现只有id的type为const。

给name字段加上唯一索引(必须要是唯一索引,普通索引不行):

create unique index user_name_unique on user(name);


测试完毕删除唯一索引:

drop index user_name_unique on user;
  • eq_ref:使用主键的关联查询,并且表中只有一条记录与主表匹配;
explain select * from user u left join role r on u.role_id=r.id;


代表有其他表引用了r表的主键。

如果主表有多条记录与之匹配那么type将不再是eq_ref

  • 首先查看两个表的数据:
mysql> select * from user;
+----+------+---------+
| id | name | role_id |
+----+------+---------+
|  1 | zs   |       1 |
+----+------+---------+
1 row in set (0.00 sec)mysql> select * from role;
+----+--------+
| id | name   |
+----+--------+
|  1 | 保洁   |
|  2 | 保安   |
|  3 | 厨师   |
+----+--------+
3 rows in set (0.00 sec)mysql> explain select * from user u left join role r on u.role_id=r.id;
+----+-------------+-------+------------+--------+---------------+---------+---------+----------------+------+----------+-------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref            | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+----------------+------+----------+-------+
|  1 | SIMPLE      | u     | NULL       | ALL    | NULL          | NULL    | NULL    | NULL           |    1 |   100.00 | NULL  |
|  1 | SIMPLE      | r     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.u.role_id |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+--------+---------------+---------+---------+----------------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)mysql>

user表示是1条记录,role是3条记录

  • 在user表中新增一条记录,再次查看执行计划:
mysql> select * from user;
+----+------+---------+
| id | name | role_id |
+----+------+---------+
|  1 | zs   |       1 |
|  2 | ls   |       1 |
+----+------+---------+
2 rows in set (0.00 sec)mysql> select * from role;
+----+--------+
| id | name   |
+----+--------+
|  1 | 保洁   |
|  2 | 保安   |
|  3 | 厨师   |
+----+--------+
3 rows in set (0.00 sec)mysql> explain select * from user u left join role r on u.role_id=r.id;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | u     | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |   100.00 | NULL                                               |
|  1 | SIMPLE      | r     | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL |    3 |   100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)mysql>

发现type字段变为了ALL,而不是原来的eq_ref

  • ref:通过非唯一索引查询到的数据

创建普通索引:

create index user_name_index on user(name);

查询执行计划:

explain select * from user where name='zs';


测试完毕删除索引:

drop index user_name_index on user;
  • range:使用索引的范围查询(普通列的范围查询不会是range)

我们执行如下两句sql查看执行计划:

explain select * from user u where u.id>20;               -- 使用索引列进行范围查询explain select * from user u where u.role_id>20;       -- 使用普通列进行范围查询


给role_id列添加索引,再次执行sql,查看执行计划:

create index user_role_id_index on user(role_id);            explain select * from user u where u.role_id>20;

  • index:查询的是索引列,遍历了索引树
explain select id from user;

  • ALL:效率最低,遍历全表
explain select * from user;


查询效率从高到底的取值为:

-- 所有的type字段取值:
NULL > system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL-- 一般情况下type字段取值:
system > const > eq_ref > ref > range > index > ALL

1.6 possible_keys 字段

查询语句中,可能应用到的索引,并非实际使用到的索引。实际使用到的索引根据key字段来反应。

例如:

-- 给name列加索引
create index idx_name on user(name); explain select * from user where user='1' or user='2';

可能用到了idx_name索引,但实际没有使用到。

测试完毕删除索引:

drop index idx_name on user;

1.7 key 字段

key字段反应sql语句实际使用的索引,为null代表没有使用索引

-- 根据id查询
explain select * from user where id=1;-- 根据普通列查询
explain select * from user where name='zs';-- 给name列加上索引
create index idx_name on user(name);-- 根据索引查询
explain select * from user where name='zs';


测试完毕删除索引:

drop index idx_name on user;

1.8 key_len 字段

表示索引中使用的字节数

explain select * from user where id=1;

我的id类型为int类型,因此占用4个字节。

我们把id类型改为bigint(Long),再次查看索引使用字节数:

alter table user modify column id bigint;explain select * from user where id=1;


测试完毕更改回来:

alter table user modify column id int;

1.9 ref 字段

表示某表的某个字段引用到了本表的索引字段

mysql> select * from user;
+----+------+---------+
| id | name | role_id |
+----+------+---------+
|  1 | zs   |       1 |
+----+------+---------+
1 row in set (0.00 sec)mysql> select * from role;
+----+--------+
| id | name   |
+----+--------+
|  1 | 保洁   |
|  2 | 保安   |
|  3 | 厨师   |
+----+--------+
3 rows in set (0.00 sec)mysql> explain select * from user u left join role r on u.role_id=r.id;
+----+-------------+-------+------------+--------+---------------+---------+---------+----------------+------+----------+-------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref            | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+----------------+------+----------+-------+
|  1 | SIMPLE      | u     | NULL       | ALL    | NULL          | NULL    | NULL    | NULL           |    1 |   100.00 | NULL  |
|  1 | SIMPLE      | r     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.u.role_id |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+--------+---------------+---------+---------+----------------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)mysql>

表示u表的role_id引用了本表(r表)的索引字段(PRIMARY)

使用其他索引列关联表:

create index role_name_index on role(name);      -- 给name列加索引。explain select * from user u left join role r on u.name=r.name;   -- 使用name列来关联

表示u表的name字段引用了本表(r表)的索引字段(role_name_index

测试完毕删除索引:

drop index role_name_index on role;

1.10 rows 字段

根据表统计信息及选用情况,大致估算出找到所需的记录或所需读取的行数。

explain select * from user;explain select * from role;

user表中有1条记录,role表中有3条记录。

1.11 filtered 字段

返回结果与实际结果的差值占总记录数的百分比

insert into user values(2,'ls','4');explain select * from user u inner join role r on u.role_id=r.id;

r表实际记录3条,上述sql语句关联查询出来的结果只能得出一条结果集,因此命中率为33.33%。

查询此SQL语句查询的记录数。

select * from user u inner join role r on u.role_id=r.id;

只有一条记录

1.12 extra 字段

显示其他扩展信息

  • Using filesort:排序时无法使用到索引时,就会出现这个。常见于order by和group by语句中。效率低
explain select name from user order by name;

  • Using temporary:表示SQL语句的操作使用到了临时表。
explain select name from user group by name;

  • Using index:代表使用到了索引,效率高
create index user_name_index on user(name);          -- 创建索引explain select name from user order by name;


测试完毕删除索引:

drop index user_name_index on user;
  • Using where:扫描全表。通常是查询条件中不是索引字段。
explain select * from user where name='zs';

  • NULL:没有用到额外的附加条件
explain select * from user where id=1;

性能对比:

Using index > NULL > Using where >= Using temporary > Using filesort

二、总结

MySQL执行计划的内容是SQL调优时的一个重要依据,我们想要优化SQL语句就必须得先掌握执行计划。这一篇主要是理论知识,也没什么好总结的。

总结一句话吧,想做MySQL调优,执行计划是必须要掌握的,切记切记!

好了,本篇就说到这里了,看完觉得有帮助的童鞋记得点赞!点赞!点赞!(重要的事情说三遍)

MySQL执行计划(MySQL调优的重要利器)相关推荐

  1. [数据库] ------ mysql 执行计划

    mysql 执行计划 简单来说,mysql整体架构分为三块:应用层,逻辑层,物理层 应用层:负责与客户端交互,建立连接,返回数据,响应请求. 逻辑层:负责查询处理,事务管理等 物理层:实际物理磁盘上存 ...

  2. mysql 执行计划extra_mysql执行计划explain type和extra

    mysql执行计划,搞定type和extra就能优化大部分sql了.type为主,extra为辅. type: system表只有一行,MyISAM引擎. const常量连接,表最多只有一行匹配,通用 ...

  3. 引导mysql执行计划_Mysql执行计划详解

    --歌手表 CREATE TABLE`singer` ( `id`int(11) unsigned NOT NULL, `update_time`datetime DEFAULT NULL, `nam ...

  4. mysql 执行计划 什么用_简述MySQL的执行计划的作用及使用方法

    作为程序员,难免要和数据库打交道,一般情况下,我们不是DBA ,但是又要写很多SQL,因此SQL效率就成了很大的问题.关于SQL效率优化,除了要掌握一定优化技巧外, 还得有很多经验的积累,但是这里我们 ...

  5. mysql 执行计划extra_MySQL执行计划extra中的using index 和 using where using index 的区别...

    www.linuxidc.com/Linux/2017-- mysql执行计划中的extra列中表明了执行计划的每一步中的实现细节,其中包含了与索引相关的一些细节信息 其中跟索引有关的using in ...

  6. mysql执行计划extra为null_MySQL执行计划extra解析

    MySQL执行计划extra解析 mysql的执行计划最让人难以捉磨的地方就是extra栏位的提示了,这是由于其他的 栏位没有提供详细的信息,因此多一个栏位来附加额外的信息,以利于用户更好的理解 sq ...

  7. MySQL执行计划解读

    MySQL执行计划解读 http://www.cnblogs.com/ggjucheng/archive/2012/11/11/2765237.html MySQL执行计划解读 Explain语法 E ...

  8. mysql执行计划字段解析_MySQL执行计划解析

    前言 在实际数据库项目开发中,由于我们不知道实际查询时数据库里发生了什么,也不知道数据库是如何扫描表.如何使用索引的,因此,我们能感知到的就只有SQL语句的执行时间.尤其在数据规模比较大的场景下,如何 ...

  9. MySQL执行计划解析

    前言 在实际数据库项目开发中,由于我们不知道实际查询时数据库里发生了什么,也不知道数据库是如何扫描表.如何使用索引的,因此,我们能感知到的就只有SQL语句的执行时间.尤其在数据规模比较大的场景下,如何 ...

最新文章

  1. 解决Ubuntu14.04 下 E: Encountered a section with no Package: header 问题
  2. 智慧城市建设面临“三座大山” 安全与服务需两手抓
  3. openfire 插件开发例子
  4. maven的profile详解
  5. [css] 说说响应式设计(responsive design)和自适应设计(adaptive design)的区别?
  6. JReBel激活码注册申请--方便Java开发中使用Jrebel热部署
  7. hdu-5703 Desert(水题)
  8. Unity使用TextMeshPro显示字体
  9. C# Socket通信服务器编写
  10. 【论文阅读】3D-CVF: Generating Joint Camera and LiDAR Features Using Cross-View Spatial Feature Fusion for
  11. 以阿里IoT开发物联网和应用平台
  12. Python特定场景数据内型“解压”操作
  13. 使用 ROT13 算法加密解密数据
  14. 分析iphone11销售数据
  15. 利用集电极负反馈到基极,以及添加电阻的方法增加中间级放大器的稳定性方法
  16. 笔记本未指定打印机服务器,打印机出现在未指定里怎么办?可以这样解决
  17. Servlet概念性回顾(结合Ajax)
  18. 机器人巨头争霸,谁主沉浮?
  19. win10双显示器,不论设置1还是2为主显示器,背景中,右键只显示“为监视器2设置”?
  20. Unicode HOWTO 中文翻译

热门文章

  1. 【DirectX11】第七篇 光照模型——环境光
  2. 职场感悟-努力与回报与个人价值
  3. Intellij idea 的tomcat原理讲解
  4. 全面解析若依框架(springboot-vue前后分离--后端部分)
  5. 不负青春再出发青年教师赴美国北亚利桑那大学
  6. 高项论文(范围管理)
  7. 使用Pyhive执行insert遇到的坑
  8. Hough变换及MATLAB示例
  9. Linux 7中安装达梦数据库DM7
  10. 统计一段文本中每个单词出现的次数(以单词为键,单词出现的次数为值)