[MySQL] Explain explain the Index Tuning combat

table of Contents

1, the use of table

2, explain the columns

2.1 id column

1) Simple subqueries

2) from the subquery clause

3) union query

2.2 select_type列

2.3 table column

2.4 type column

2.5 possible_keys列

2.6 key column

2.7 key_len 列

2.8 ref column

2.9 rows columns

2.10 Extra column

3, index optimization combat

Table 2.1 Use of

2.2 index optimization principles


EXPLAIN command (implementation plan) is how to view the optimizer to determine the main method for executing the query. Use EXPLAIN keyword can simulate the optimizer to execute SQL statements, so they know how to handle your MySQL is a SQL statement, let us know SQL execution plan , can help us understand MySQL's cost-based optimizer, you can also get a lot of possible details to be considered optimizer access policy, as well as when running SQL statements which strategy is expected to be adopted optimizer, and then analyze your query performance bottlenecks or structure.

 

The following are examples explain the use of: 

Increased explain keywords before the select statement, MySQL will set a mark on the query, the query is executed, returns execution plan information, rather than execute this SQL (if from contains sub-queries will execute the sub-queries, the results into a temporary table)

 

1 , the use of table

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');
 
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');
 
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);

mysql> explain select * from actor;

Each table in the query will output a line, if there are two tables through a join join query, then the output will be two lines. Meaning the table is quite broad: can be sub-queries, the results of a union and so on.

 

explain There are two variants:

1) explain Extended : extra query optimization provide some information on the basis of explain. Followed by the query can be optimized by show warnings commands to see what the optimizer optimized. Additionally, filtered column, is a half-point value ratio, rows * filtered / 100 can estimate the number of lines to and a table explain front connection (the previous table refer to the id value explain is smaller than the current table id value table).

 

mysql> explain extended select * from film where id = 1;

 

MySQL > Show Represents warnings; - this is to give current opinions select automatic optimization through the system, of course, the views are not necessarily accurate, for reference only

2) explain partitions : partitions compared to explain more than a field, if the query is based on the partition table, it will display the partition query access.

 

2 , EXPLAIN columns

 

Column Name

 

description

 

id

 

In a large query SELECT statement for each keyword corresponds to a unique id

 

select_type

 

Type the keyword corresponding SELECT query

 

table

 

Table Name

 

partitions

 

Matching partition information

 

type

 

For single-table access method

 

possible_keys

 

It may be used in the index

 

key

 

In fact the use of the index

 

key_len

 

The actual length of the index

 

ref

 

When an index column equivalent queries, object-equivalent information matches the index column

 

rows

 

Record the estimated number of required reading

 

filtered

 

The percentage number of pieces remaining recording filtered through a table search criteria

 

Extra

 

Some additional information

Next we will show the information explain in each column.

 

2.1 id column

id number of the column is select the serial number, there are a few select few id, id and order is the order of appearance of select growth. MySQL will select queries into simple queries (SIMPLE) and complex queries (PRIMARY).

Complex queries are divided into three categories: simple subqueries , derived tables (from the sub-query statements), of Union query .

id column the greater the higher execution priority, id from executing the same downward, id final implementation is NULL

 

1 ) Simple subqueries

mysql> explain select (select 1 from actor limit 1) from film;

 

2 ) from the subquery clause

mysql> explain select id from (select id from film) as der;

这个查询执行时有个临时表别名为der,外部 select 查询引用了这个临时表

 

3union查询

mysql> explain select 1 union all select 1;

union结果总是放在一个匿名临时表中,临时表不在SQL中出现,因此它的id是NULL。

 

2.2 select_type

select_type 表示对应行是简单还是复杂的查询,如果是复杂的查询,又是上述三种复杂查询中的哪一种。

1)simple:简单查询。查询不包含子查询和union

mysql> explain select * from film where id = 2;

 

2)primary:复杂查询中最外层的 select

 

3)subquery:包含在 select 中的子查询(不在 from 子句中)

 

4)derived:包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表(derived的英文含义)

用这个例子来了解 primary、subquery 和 derived 类型

mysql> explain select (select 1 from actor where id = 1) from (select * from film where id = 1) der;

<derived3>后面的3表示的是select的ID,也就是是ID为3的DERIVED类型的select生成的派生表 。

 

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

 

6)union result:从 union 临时表检索结果的 select

用这个例子来了解 union 和 union result 类型:

mysql> explain select 1 union all select 1;

 

2.3 table

这一列表示 explain 的一行正在访问哪个表。

  • 当 from 子句中有子查询时,table列是 <derivenN> 格式(派生表),表示当前查询依赖 id=N 的查询,于是先执行 id=N 的查询。
  • 当有 union 时,UNION RESULT 的 table 列的值为<union1,2>,1和2表示参与 union 的 select 行id。

 

2.4 type

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

依次从最优到最差分别为:system > const > eq_ref > ref > range > index > ALL

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

 

NULL:mysql能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引。例如:在索引列中选取最小值,可以单独查找索引来完成,不需要在执行时访问表

mysql> explain select min(id) from film;

 

const, system:mysql能对查询的某部分进行优化并将其转化成一个常量(可以看show warnings 的结果)。用于 primary key 或 unique key 的所有列与常数比较时,所以表最多有一个匹配行,读取1次,速度比较快。systemconst的特例,表里只有一条元组匹配时为system

mysql> explain extended select * from (select * from film where id = 1) tmp;

mysql> show warnings;

 

eq_ref:primary key 或 unique key 索引的所有部分被连接使用 ,最多只会返回一条符合条件的记录。这可能是在 const 之外最好的联接类型了,简单的 select 查询不会出现这种 type。

mysql> explain select * from film_actor left join film on film_actor.film_id = film.id;

 

ref:相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行。

1. 简单 select 查询,name是普通索引(非唯一索引)

mysql> explain select * from film where name = "film1";

 

2.关联表查询,idx_film_actor_id是film_id和actor_id的联合索引,这里使用到了film_actor的左边前缀film_id部分。

mysql> explain select film_id from film left join film_actor on film.id = film_actor.film_id;

 

range:范围扫描通常出现在 in(), between ,> ,<, >= 等操作中。使用一个索引来检索给定范围的行。

mysql> explain select * from actor where id > 1;

 

index:扫描全表索引,这通常比ALL快一些。(index是从索引中读取的,而all是从硬盘中读取)

mysql> explain select * from film;

 

ALL:即全表扫描,意味着mysql需要从头到尾去查找所需要的行。通常情况下这需要增加索引来进行优化了

mysql> explain select * from actor;

 

2.5 possible_keys

这一列显示查询可能使用哪些索引来查找。 

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

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

 

2.6 key

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

如果没有使用索引,则该列是 NULL。如果想强制mysql使用或忽视possible_keys列中的索引,在查询中使用 force index、ignore index。

 

2.7 key_len

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

举例来说,film_actor的联合索引 idx_film_actor_id 由 film_id 和 actor_id 两个int列组成,并且每个int是4字节。通过结果中的key_len=4可推断出查询使用了第一个列:film_id列来执行索引查找。

mysql> explain select * from film_actor where film_id = 2;

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

 

索引最大长度是768字节,当字符串过长时,mysql会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索引。

 

2.8 ref

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

 

2.9 rows

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

 

2.10 Extra

这一列展示的是额外信息。常见的重要值如下: 

Using index:查询的列被索引覆盖,并且where筛选条件是索引的前导列,是性能高的表现。一般是使用了覆盖索引(索引包含了所有查询的字段)。对于innodb来说,如果是辅助索引性能会有不少提高。索引优化的目的就是尽量让Extra列是Using index,这样查询效率最高

mysql> explain select film_id from film_actor where film_id = 1;

 

Using where:查询的列未被索引覆盖,where筛选条件非索引的前导列

mysql> explain select * from actor where name = 'a';

 

Using where Using index:查询的列被索引覆盖,并且where筛选条件是索引列之一但是不是索引的前导列,意味着无法直接通过索引查找来查询到符合条件的数据

mysql> explain select film_id from film_actor where actor_id = 1;

 

NULL:查询的列未被索引覆盖,并且where筛选条件是索引的前导列,意味着用到了索引,但是部分字段未被索引覆盖,必须通过“回表”来实现,不是纯粹地用到了索引,也不是完全没用到索引

mysql>explain select * from film_actor where film_id = 1;

 

Using index condition:与Using where类似,查询的列不完全被索引覆盖,where条件中是一个前导列的范围;

mysql> explain select * from film_actor where film_id > 1;

 

 

Using temporary:mysql需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的,首先是想到用索引来优化。

1. actor.name没有索引,此时创建了张临时表来distinct distinct:消除取值重复的行

mysql> explain select distinct name from actor;

 

2. film.name建立了idx_name索引,此时查询时extrausing index,没有用临时表

mysql> explain select distinct name from film;

 

Using filesort(文件排序):mysql 会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。此时mysql会根据联接类型浏览所有符合条件的记录,并保存排序关键字和行指针,然后排序关键字并按顺序检索行信息。这种情况下一般也是要考虑使用索引来优化的,因为文件排序效率比索引排序低很多。总之就是在使用order by排序的时候,如果排序字段没有创建索引,也就无法使用现成的已经排序好的数据结构(叶子结点的内容已经是排好序依次连接了),就只能去使用文件排序来实现order by。

1. actor.name未创建索引,会浏览actor整个表,保存排序关键字name和对应的id,然后排序name并检索行记录

mysql> explain select * from actor order by name;

 

2. film.name建立了idx_name索引,此时查询时extrausing index

mysql> explain select * from film order by name;

 

3、索引优化实战

2.1 使用的表

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());

 

2.2 索引优化原则

1. 全值匹配

EXPLAIN SELECT * FROM employees WHERE name= 'LiLei';

 

EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22;

 

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

 

2.最左前缀法则

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

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

EXPLAIN SELECT * FROM employees WHERE position = 'manager';

EXPLAIN SELECT * FROM employees WHERE name = 'LiLei';

 

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

EXPLAIN SELECT * FROM employees WHERE name = 'LiLei';

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

 

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)当覆盖索引指向的字段是varchar(380)及380以上的字段时,覆盖索引会失效!

 

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

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

EXPLAIN SELECT * FROM employees WHERE name = 1000;

 

10.少用or,用它连接时很多情况下索引会失效

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

 

总结:

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


其他相关文章:【MySQL】慢查询的配置与使用
                        【MySQL】MySQL的存储引擎和索引详解(聚集索引和非聚集索引)
                        【MySQL】InnoDB行格式、数据页结构以及索引底层原理分析
                        【MySQL】InnoDB存储引擎,MyISAM存储引擎,聚集索引,非聚集索引,主键索引,二级索引他们之间的关系梳理
                        【MySQL】InnoDB 的索引模型(B+树)

发布了54 篇原创文章 · 获赞 47 · 访问量 1万+

Guess you like

Origin blog.csdn.net/cy973071263/article/details/104551865