mysql优化工具(explain)

Explain工具介绍

使用EXPLAIN关键字可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈在 select 语句之前增加 explain 关键字,MySQL 会在查询上设置一个标记,执行查询会返回执行计划的信息,而不是执行这条SQL注意:如果 from 中包含子查询,仍会执行该子查询,将结果放入临时表中

Explain分析示例1

示例表:

表1

DROP TABLE IF EXISTS `actor`;
CREATE TABLE `actor` (`id` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
`update_time` datetime DEFAULT NULL,PRIMARY KEY (`id`))
ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES
(1,‘a‘,‘2017‐12‐22 15:27:18‘),(2,‘b‘,‘2017‐12‐22 15:27:18‘),(3,‘c‘,‘2017‐12‐22 15:27:18‘);

表2

DROP TABLE IF EXISTS `film`;
CREATE TABLE `film` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `film` (`id`, `name`) VALUES (3,‘film0‘),(1,‘film1‘),(2,‘film2‘);

表3

DROP TABLE IF EXISTS `film_actor`;
CREATE TABLE `film_actor` (
`id` int(11) NOT NULL,
`film_id` int(11) NOT NULL,
`actor_id` int(11) NOT NULL,
`remark` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_film_actor_id` (`film_id`,`actor_id`))
ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (1,1,1),(2,1,2),(3,2,1);

在查询中的每个表会输出一行,如果有两个表通过 join 连接查询,那么会输出两行

explain 两个变种

1)explain extended:会在 explain 的基础上额外提供一些查询优化的信息。紧随其后通过 show warnings 命令可以得到优化后的查询语句,从而看出优化器优化了什么

查询主键:

explain select * from pea_goods where pea_goods_Id=190919181614003;

输出结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE pea_goods const PRIMARY PRIMARY 8 const 1 100.00

show warnings;

输出结果

Level Code Message
Note 1003 /* select#1 */ select ‘190919181614003‘ AS pea_goods_id,‘190919145343001‘ AS pea_supplier_id,‘190919175230001‘ AS pea_customer_id,‘舒乐安定‘ AS goods_name,‘XXX007‘ AS goods_code,‘10‘ AS goods_type,‘吨‘ AS goods_unit,NULL AS safe_date,‘100‘ AS width,‘100‘ AS length,‘100‘ AS height,‘100‘ AS weight,NULL AS volume,‘10000.08‘ AS price,‘2019-10-30 23:59:59‘ AS warranty,‘30‘ AS allowed_day,‘危险品,小心被炸‘ AS description,NULL AS line_no,‘Y‘ AS is_active,‘190815135958005‘ AS created_by,‘2019-09-19 18:16:14‘ AS created,‘190815135958005‘ AS updated_by,‘2019-10-10 16:53:02‘ AS updated,‘190815135742002‘ AS ad_client_id from vip_tulin.pea_goods where 1

查询非主键

explain select * from pea_goods where pea_customer_id=190919175230001;

输出结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE pea_goods ALL 50 10.00 Using where

show warnings;

输出结果

Level Code Message
Note 1003 /*
select#1 */ select `vip_tulin`.`pea_goods`.`pea_goods_id` AS `pea_goods_id`,`vip_tulin`.`pea_goods`.`pea_supplier_id` AS `pea_supplier_id`,`vip_tulin`.`pea_goods`.`pea_customer_id` AS `pea_customer_id`,`vip_tulin`.`pea_goods`.`goods_name` AS `goods_name`,`vip_tulin`.`pea_goods`.`goods_code` AS `goods_code`,`vip_tulin`.`pea_goods`.`goods_type` AS `goods_type`,`vip_tulin`.`pea_goods`.`goods_unit` AS `goods_unit`,`vip_tulin`.`pea_goods`.`safe_date` AS `safe_date`,`vip_tulin`.`pea_goods`.`width` AS `width`,`vip_tulin`.`pea_goods`.`length` AS `length`,`vip_tulin`.`pea_goods`.`height` AS `height`,`vip_tulin`.`pea_goods`.`weight` AS `weight`,`vip_tulin`.`pea_goods`.`volume` AS `volume`,`vip_tulin`.`pea_goods`.`price` AS `price`,`vip_tulin`.`pea_goods`.`warranty` AS `warranty`,`vip_tulin`.`pea_goods`.`allowed_day` AS `allowed_day`,`vip_tulin`.`pea_goods`.`description` AS `description`,`vip_tulin`.`pea_goods`.`line_no` AS `line_no`,`vip_tulin`.`pea_goods`.`is_active` AS `is_active`,`vip_tulin`.`pea_goods`.`created_by` AS `created_by`,`vip_tulin`.`pea_goods`.`created` AS `created`,`vip_tulin`.`pea_goods`.`updated_by` AS `updated_by`,`vip_tulin`.`pea_goods`.`updated` AS `updated`,`vip_tulin`.`pea_goods`.`ad_client_id` AS `ad_client_id` from `vip_tulin`.`pea_goods` where (`vip_tulin`.`pea_goods`.`pea_customer_id` = 190919175230001)

id 列

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE pea_goods ALL 50 10.00 Using where
  id 列的编号是select 的序列号,有几个select 就有几个id,并且顺序是按照select 顺序增长的。
  id 越大执行优先级越高,id 相同从上往下执行,id 为null 最后执行。

2. select_type 列

simple : 表示简单查询 查询不包括 子查询和union

primary: 复杂查询的最外层的select

subquery: select 子句中的子查询

derived: from 子句的子查询

sql :

set session optimizer_switch=‘derived_merge=off‘;
-- 关闭 mysql 5.7 新特性对衍生表的合并优化
EXPLAIN SELECT
	( SELECT 1 FROM pea_goods WHERE pea_goods_id = 190919182516001 )
FROM
	( SELECT * FROM pea_customer WHERE pea_customer_id = 190919175230001 ) goods

比较 primary、subquery 和 derived 类型 >>输出结果:

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY system 1 100.00
3 DERIVED pea_customer const PRIMARY PRIMARY 8 const 1 100.00
2 SUBQUERY pea_goods const PRIMARY PRIMARY 8 const 1 100.00

union:在 union 中的第二个和随后的 select

输出结果:

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY No tables used
1 UNION

3. table 列

这一列表示xeplain 的一行正在访问那个表。

from 有子查询的时候 ,table 列 是 表示当前查询依赖 id=N 查询,于是先执行id=N查询

当有union ,UNION RESULT的table列 值 为<union 1,2> 1和2 表示参与union的select 和 id。

4. type 列

这一列表示 关联类型或者访问类型,就是 mysql 决定如何查找 表中的行,查找数据行记录的大概范围。

从最优到最差 system>const>eq_ref>ref>rangge>index>ALL

一般来讲 :得保证查询达到range级别,最好达到ref

NULL : mysql 能够 在优化阶段查询语句,在执行阶段不用再访问或索引 。

const ,system : mysql 能对查询的某部分进行优化并将其转化成一个常量

(show warning) 用于primary key 或 unique key 的所有列与常数比较时,所以表最多有一个匹配行,读取一次,速度比较快。system是const 的特例。表里只有一条元祖匹配时为system.

5. possible_keys 列

这一列 显示可能使用那些索引来查找

explain 时可能出现possible_keys 有列,而key 显示NULL的情况,这种情况是因为,表中数据不多,mysql 认为索引对此查询帮助不大,选择了全表查询

如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查 where 子句看是否可以创造一个适当的索引来提高查询性能,然后用 explain 查看效果

6 key 列

这一列 显示mysql 实际采用哪一个索引来优化对该表的访问,

如果没有使用索引该列为NULL, 如果想强制或忽略 possible_key列中的索引,在查询中使用force_index,ignore_index.

7.key_len 列

这一列显示了mysql 在索引里使用的字节数,通过这个数可以算出来具体索引中的哪些列。

pea_goods_Id 为 bigint 类型 并且是8个字节。

查询主键:

explain select * from pea_goods where pea_goods_Id=190919181614003;

输出结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE pea_goods const PRIMARY PRIMARY 8 const 1 100.00

key_len计算规则如下:

字符串
  • char(n): n字节长度
  • varchar(n):2字节存储字符串长度,如果是utf-8,则长度 3n+ 2
数值类型
  • tinyint:1字节
  • smallint:2字节
  • int:4字节
  • bigint:8字节
时间类型
  • date:3字节
  • timestamp:4字节
  • datetime:8字节如果字段允许为 NULL,需要1字节记录是否为 NULL

8 ref 列

这一列显示了在key列记录的索引中,表查找值所用到的列或常量,常见的有:const(常量),字段名(例:film.id

9 rows 列

这一列是mysql 估计要读取并检测的行数, 注意这个不是结果集里的行数

10 Extra 列

这一列 展示额外信息,常见的重要的指 如下

  • Using index : 使用覆盖索引
  • Using where : 使用where 语句来处理结果,查询列未被索引覆盖
  • Using index condition :查询的列不完全被索引覆盖 ,where 条件中是一个前导列的范围。
  • Using temporary : mysql 需要创建一张临时表来处理查询,出现这种情况一般要进行优化的,首先是想到用索引来优化。
    1. goods_name 无索引 此时创建了一张临时表

    EXPLAIN SELECT DISTINCT goods_name from pea_goods

无索引 结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY pea_goods ALL Using temporary
EXPLAIN SELECT DISTINCT  goods_name from  pea_goods

有索引结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY pea_goods ALL Using index
  • Using filesort

将用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序。这种情况下一般也是要考虑使用索引来优化的

  1. goods_name 未创建索引,pea_goods,保存排序关键字pea_goods和对应的id,然后排序pea_goods并检索行记录

    EXPLAIN SELECT * from pea_goods ORDER BY goods_name

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY pea_goods ALL Using filesort
  1. goods_name 创建索引
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY pea_goods ALL Using index
  • Select tables optimized away :

使用某些聚合函数(比如 max、min)来访问存在索引的某个字段是

EXPLAIN SELECT min(pea_goods_id) from pea_goods ORDER BY goods_name

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE Select tables optimized away

索引最佳实践

创建表

CREATE TABLE `employees` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`name` VARCHAR ( 24 ) NOT NULL DEFAULT ‘‘ COMMENT ‘姓名‘,
`age` INT ( 11 ) NOT NULL DEFAULT ‘0‘ COMMENT ‘年龄‘,
`position` VARCHAR ( 20 ) NOT NULL DEFAULT ‘‘ COMMENT‘职位‘,
`hire_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ‘入职时间‘,
PRIMARY KEY ( `id` ), KEY `idx_name_age_position` ( `name`, `age`, `position` ) USING BTREE  )
ENGINE = INNODB AUTO_INCREMENT = 4 DEFAULT
CHARSET = utf8 COMMENT = ‘员工记录表‘; 

INSERT INTO employees ( NAME, age, position, hire_time ) VALUES ( ‘LiLei‘,22,‘manager‘,NOW());
INSERT INTO employees ( NAME, age, position, hire_time ) VALUES (‘HanMeimei‘, 23, ‘dev‘, NOW());
INSERT INTO employees ( NAME, age, position, hire_time ) VALUES (‘Lucy‘,23,  ‘dev‘, NOW());

1.全值匹配

1. EXPLAIN SELECT *  FROM     employees  WHERE     NAME = ‘LiLei‘;
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE employees ref idx_name_age_position idx_name_age_position 74 const 1
EXPLAIN SELECT     *  FROM     employees  WHERE
NAME = ‘LiLei‘      AND age = 22;
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE employees ref idx_name_age_position idx_name_age_position 78 const,const 1
EXPLAIN SELECT     *  FROM     employees  WHERE     NAME = ‘LiLei‘ AND age = 22
ANDposition = ‘manager‘;
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE employees ref idx_name_age_position idx_name_age_position 140 const,const,const 1

2.最左前缀法则

如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。

EXPLAIN SELECT * FROM employees WHERE age = 22 AND position =‘manager‘;

结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE employees 3 33.33 Using where
EXPLAIN SELECT * FROM employees WHERE position = ‘manager‘;

结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE employees 3 33.33 Using where
EXPLAIN SELECT * FROM employees WHERE name = ‘LiLei‘;

结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE employees ref idx_name_age_position idx_name_age_position 74 const 1 100.00

3.不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描

EXPLAIN SELECT * FROM employees WHERE name =‘LiLei‘;EXPLAIN SELECT * FROM employees
WHERE left(name,3) = ‘LiLei‘;

结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE employees ref idx_name_age_position idx_name_age_position 74 const 1 100.00

给hire_time增加一个普通索引:

   ALTER TABLE `employees`2ADD INDEX `idx_hire_time` (`hire_time`) USING BTREE ;
    EXPLAIN  select * from employees where date(hire_time) =‘2018-09-30‘;

结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE employees 3 100.00 Using where

转化为日期范围查询,会走索引:

EXPLAIN  select * from employees where hire_time >=‘2018-09-30 00:00:00‘
andhire_time <=‘2018-09-30 23:59:59‘;

还原最初索引状态

ALTER TABLE `employees` DROP INDEX `idx_hire_time`;

4.存储引擎不能使用索引中范围条件右边的列

EXPLAIN SELECT * FROM employees WHERE name= ‘LiLei‘ AND age = 22 AND position =‘manager‘;
EXPLAIN SELECT * FROM employees WHERE name= ‘LiLei‘ AND age > 22 AND position =‘manager‘;

5. 尽量使用覆盖索引(只访问索引的查询(索引列包含查询列)),减少select *语句

EXPLAIN SELECT name,age FROM employees WHERE name= ‘LiLei‘ AND age = 23 AND position =‘manager‘;
EXPLAIN SELECT * FROM employees WHERE name= ‘LiLei‘ AND age = 23 AND position =‘manager‘;

6. mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描

EXPLAIN SELECT * FROM employees WHERE name != ‘LiLei‘;

7. is null,is not null 也无法使用索引

EXPLAIN SELECT * FROM employees WHERE name is null

8. like以通配符开头(‘$abc...‘)mysql索引失效会变成全表扫描操作

EXPLAIN SELECT * FROM employees WHERE name like ‘%Lei‘
EXPLAIN SELECT * FROM employees WHERE name like ‘Lei%‘
问题:解决like‘%字符串%‘索引不被使用的方法?

a)使用覆盖索引,查询字段必须是建立覆盖索引字段

    EXPLAIN SELECT name,age,position FROM employees WHERE name like ‘%Lei%‘;

b)如果不能使用覆盖索引则可能需要借助搜索引擎

9.字符串不加单引号索引失效

EXPLAIN SELECT * FROM employees WHERE name = ‘1000‘;
EXPLAIN SELECT * FROM employees WHERE name = 1000;

10.少用or或in

用它查询时,mysql不一定使用索引,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引,详见范围查询优化

EXPLAIN SELECT * FROM employees WHERE name = ‘LiLei‘ or name = ‘HanMeimei‘;

11.范围查询优化给年龄添加单值索引

ALTER TABLE `employees`2ADD INDEX `idx_age` (`age`) USING BTREE ;
explain select * from employees where age >=1 and age <=2000;

没走索引原因:mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引。比如这个例子,可能是由于单次数据量查询过大导致优化器最终选择不走索引优化方法:可以讲大的范围拆分成多个小范围

explain select * from employees where age >=1 and age <=1000;
explain select * from employees where age >=1001 and age <=2000;

还原最初索引状态

1ALTER TABLE employees2DROP INDEX idx_age;

索引使用总结:

假设 index(a,b,c)

where 语句 索引是否被使用
where a=3 Y ,使用到a
where a =3 and b =4 Y ,使用到 a,b
where a =3 and b =4 and c=5 Y ,使用到 a,b,c
where b=3 / where b= 3 and c=4 /where c=5 N
where a=3 and c=5 使用到a ,但是c不可以, b中间断了
where a=3 and b like ‘KK%‘ and c=4 Y ,使用到a,b,c
where a=3 and b like ‘%KK‘ and c =4 Y 只用到a
where a =3 and b like ‘%KK%‘ and c=4 Y 只用到a
where a =3 and b like ‘K%KK%‘ and c=4 Y 用到a,b,c

like KK%相当于=常量,%KK和%KK% 相当于范围

原文地址:https://www.cnblogs.com/wxs121/p/12550107.html

时间: 2024-10-13 01:35:46

mysql优化工具(explain)的相关文章

mysql 优化工具

explain  profiling 建议提供以下信息 show table status like 'audit';show create table audit;show index from audit;check table audit; analyze table audit; analyze 并不检查表是否有问题,只是重新分析一下键的分布情况.check table/ repaire table 是检查和修复的语句 SELECT * FROM [TABLE] FORCE INDEX

MySQL优化工具之 profiling

MySQL优化工具之profiling 使用慢查询日志分析出慢查询语句后,用profiling分析该语句的优化后执行效果. 查看慢查询设置 mysql> show variables like "%slow%"; +---------------------+---------------------------------+ | Variable_name       | Value                           | +------------------

MySQL优化之explain

在日常的MYSQL优化中我们常常看到这样一个关键词:explain,例如这种: EXPLAIN SELECT * FROM Cloud_Order WHERE money < 10; explain是什么呢?使用 EXPLAIN 关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的.这可以帮你分析你的查询语句或是表结构的性能瓶颈.通过explain命令可以得到: 表的读取顺序 数据读取操作的操作类型 哪些索引可以使用 哪些索引被实际使用 表之间的引用 每张表有多少

在Centos环境下安装mysql优化工具:pt-query-digest

事前说明,本人使用的是虚拟机做CentOS6.7,主机是windows10,主要用CentOS来安装软件并且操作. 作为运维工作人员掌握MySQL的"增删改查"是必须的,而再学习一点优化也是好的,MySQL自带一个叫"慢查文件"的东西,但是那玩意很基本,虽然很好操作但是能力也很有限,于是pt-query-digest 工具就应运而生,这个软件只有linux版,所以windows的用户可以关闭此页面了. 可以先看看https://www.percona.com/doc

MySQL 优化sql explain执行计划详解

mysql explain执行计划详解 1).id列数字越大越先执行,如果说数字一样大,那么就从上往下依次执行,id列为null的就表是这是一个结果集,不需要使用它来进行查询. 2).select_type列常见的有:A:simple:表示不需要union操作或者不包含子查询的简单select查询.有连接查询时,外层的查询为simple,且只有一个B:primary:一个需要union操作或者含有子查询的select,位于最外层的单位查询的select_type即为primary.且只有一个C:

MySQL优化—工欲善其事,必先利其器之EXPLAIN

转自:http://www.cnblogs.com/magialmoon/archive/2013/11/23/3439042.html mysql官方手册关于explain命名的说明文档:https://dev.mysql.com/doc/refman/5.7/en/explain-output.html#explain_select_type 最近慢慢接触MySQL,了解如何优化它也迫在眉睫了,话说工欲善其事,必先利其器.最近我就打算了解下几个优化MySQL中经常用到的工具.今天就简单介绍下

MYSQL学习笔记——sql语句优化工具

前面讲解了很多mysql的基础知识,这一章讲解mysql的语句优化. 一.定位慢查询                                                                                 我们要对sql语句进行优化,第一步肯定是找到执行速度较慢的语句,那么怎么在一个项目里面定位这些执行速度较慢的sql语句呢?下面就介绍一种定位慢查询的方法. 1.1.数据库准备 首先创建一个数据库表: CREATE TABLE emp (empno MED

mysql优化实战(explain &amp;&amp; 索引)

实验环境: 1.sql工具:Navicat 2.sql数据库,使用openstack数据库作为示例 一.mysql索引查询 show index from instances 结果字段解释: Table:数据库表名 Non_unique:索引不能包括重复词,则为0.可以,则为1. Key_name:索引的名称. 索引中的列序列号,从1开始. 列名称 列以什么方式存储在索引中.在MySQL中,有值'A'(升序)或NULL(无分类). 索引中唯一值的数目的估计值.通过运行ANALYZE TABLE或

【MySQL笔记】SQL优化利器 - explain命令的输出格式详解

有MySQL使用经验的同学在实际项目中可能会遇到SQL慢查询的场景,有些场景很容易定位问题所在(如单表操作有慢查询SQL时,仔细check SQL语句通常很容易定位索引问题),而有些复杂业务场景下(如多表联合查询几十个字段并做group或sort等操作),人工check SQL语句通常很难发现SQL瓶颈根源.这个时候,MySQL提供的explain命令就派上用场了. 本笔记主要对explain的输出结果做说明,并给出根据explain输出对SQL做优化的思路. 1. EXPLAIN语法及用途 e