MySQL执行计划(MySQL调优的重要利器)
文章目录
- 看完本篇文章你能学到什么?
- 一、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;
explain
和desc
效果一样,都是帮我们列出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调优的重要利器)相关推荐
- [数据库] ------ mysql 执行计划
mysql 执行计划 简单来说,mysql整体架构分为三块:应用层,逻辑层,物理层 应用层:负责与客户端交互,建立连接,返回数据,响应请求. 逻辑层:负责查询处理,事务管理等 物理层:实际物理磁盘上存 ...
- mysql 执行计划extra_mysql执行计划explain type和extra
mysql执行计划,搞定type和extra就能优化大部分sql了.type为主,extra为辅. type: system表只有一行,MyISAM引擎. const常量连接,表最多只有一行匹配,通用 ...
- 引导mysql执行计划_Mysql执行计划详解
--歌手表 CREATE TABLE`singer` ( `id`int(11) unsigned NOT NULL, `update_time`datetime DEFAULT NULL, `nam ...
- mysql 执行计划 什么用_简述MySQL的执行计划的作用及使用方法
作为程序员,难免要和数据库打交道,一般情况下,我们不是DBA ,但是又要写很多SQL,因此SQL效率就成了很大的问题.关于SQL效率优化,除了要掌握一定优化技巧外, 还得有很多经验的积累,但是这里我们 ...
- mysql 执行计划extra_MySQL执行计划extra中的using index 和 using where using index 的区别...
www.linuxidc.com/Linux/2017-- mysql执行计划中的extra列中表明了执行计划的每一步中的实现细节,其中包含了与索引相关的一些细节信息 其中跟索引有关的using in ...
- mysql执行计划extra为null_MySQL执行计划extra解析
MySQL执行计划extra解析 mysql的执行计划最让人难以捉磨的地方就是extra栏位的提示了,这是由于其他的 栏位没有提供详细的信息,因此多一个栏位来附加额外的信息,以利于用户更好的理解 sq ...
- MySQL执行计划解读
MySQL执行计划解读 http://www.cnblogs.com/ggjucheng/archive/2012/11/11/2765237.html MySQL执行计划解读 Explain语法 E ...
- mysql执行计划字段解析_MySQL执行计划解析
前言 在实际数据库项目开发中,由于我们不知道实际查询时数据库里发生了什么,也不知道数据库是如何扫描表.如何使用索引的,因此,我们能感知到的就只有SQL语句的执行时间.尤其在数据规模比较大的场景下,如何 ...
- MySQL执行计划解析
前言 在实际数据库项目开发中,由于我们不知道实际查询时数据库里发生了什么,也不知道数据库是如何扫描表.如何使用索引的,因此,我们能感知到的就只有SQL语句的执行时间.尤其在数据规模比较大的场景下,如何 ...
最新文章
- 解决Ubuntu14.04 下 E: Encountered a section with no Package: header 问题
- 智慧城市建设面临“三座大山” 安全与服务需两手抓
- openfire 插件开发例子
- maven的profile详解
- [css] 说说响应式设计(responsive design)和自适应设计(adaptive design)的区别?
- JReBel激活码注册申请--方便Java开发中使用Jrebel热部署
- hdu-5703 Desert(水题)
- Unity使用TextMeshPro显示字体
- C# Socket通信服务器编写
- 【论文阅读】3D-CVF: Generating Joint Camera and LiDAR Features Using Cross-View Spatial Feature Fusion for
- 以阿里IoT开发物联网和应用平台
- Python特定场景数据内型“解压”操作
- 使用 ROT13 算法加密解密数据
- 分析iphone11销售数据
- 利用集电极负反馈到基极,以及添加电阻的方法增加中间级放大器的稳定性方法
- 笔记本未指定打印机服务器,打印机出现在未指定里怎么办?可以这样解决
- Servlet概念性回顾(结合Ajax)
- 机器人巨头争霸,谁主沉浮?
- win10双显示器,不论设置1还是2为主显示器,背景中,右键只显示“为监视器2设置”?
- Unicode HOWTO 中文翻译