mysql optimization tools (explain)

Explain Tool Introduction

Use EXPLAIN keyword can simulate the optimizer to execute SQL statements, analyze the performance bottleneck in your query or keyword structure increases explain before the select statement, MySQL will set a mark on the query, execute query returns execution plan information, instead executes the SQL Note: if from contains sub-queries, will execute the sub-query results into a temporary table

Explain analysis Example 1

Example Table:

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

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

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

Each table will output a line in the query, if there are two tables join query by join, then the output will be two lines

explain 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 optimization

Primary key query:

explain select * from pea_goods where pea_goods_Id=190919181614003;

Output

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;

Output

Level Code Message
Note 1003 / * SELECT * #. 1 / SELECT '190,919,181,614,003' the AS pea_goods_id, '190,919,145,343,001' the AS pea_supplier_id, '190,919,175,230,001' the AS pea_customer_id, 'estazolam' the AS goods_name, 'XXX007' the AS goods_code, '10 'the AS goods_type,' t 'the AS goods_unit, the AS NULL safe_date,' 100 'the AS width,' 100 'the AS length,' 100 'the AS height,' 100 'the AS weight, the AS NULL volume,' 10000.08 'the AS price,' 2019-10-30 23:59:59 'the AS warranty, '30' the AS allowed_day, 'dangerous, carefully blown 'the AS description, the AS NULL line_no,' the Y 'the AS is_active,' 190,815,135,958,005 'the AS created_by,' 2019-09-19 18:16:14 'the AS created,' 190,815,135,958,005 'the AS updated_by,' 2019-10-10 16:53:02 'the AS updated, '190,815,135,742,002' the AS ad_client_idfrom vip_tulin.pea_goods where 1

Non-primary key query

explain select * from pea_goods where pea_customer_id=190919175230001;

Output

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;

Output

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 column

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 列

the Simple : represents the simple query query does not include sub-queries and of Union
Primary : the outermost select complex queries
subquery : select clause subquery
derived : from sub-query clause

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

Comparison of primary, subquery and derived types >> output:

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 : select the second and subsequent union of the

Output:

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

3. table column

This column represents xeplain row is accessing the table.
from when there are sub-queries, table column Current query indicates query id = N-dependent, so the first query execution id = N
when there is union, UNION RESULT the table column values <union 1,2> 1 and 2 show the involvement of the union and select id.

4. type column

This column shows the type of association or access type is mysql decide how to find rows in the table, find the approximate range of rows of data.
From best to worst system> const> eq_ref> ref> rangge> index> ALL In general: have to ensure that the query reaches the level range, preferably up to ref

NULL : MySQL can query in the optimization stage, do not have access or index during the implementation phase.

const, System : MySQL can be optimized for a part of the query and converts it to a constant
(show warning) when compared to all the columns and a constant primary key or a unique key for the table so that at most one matching row, read once , faster. const system is a special case. When only one list matching tuples named system.

5. possible_keys 列

This column displays may use the index to find those
that may occur when there is explain possible_keys columns, and display key to NULL, because this situation is not much data in the table, mysql think of little help index this query, select the entire table Inquire

If the column is NULL, no relevant index. In this case, you can see if you can create an appropriate index to improve query performance, then explain to view the results by checking the where clause

6 key column

This column shows which index mysql actually used to optimize access to the table,
if the index is not used as a NULL, if you want to enforce or ignore possible_key column index, use force_index, ignore_index in the query.

7.key_len 列

This column shows the number of bytes used in the index mysql, can be calculated by the number of columns which specific index.

pea_goods_Id bigint type and is 8 bytes.

Primary key query:

explain select * from pea_goods where pea_goods_Id=190919181614003;

Output

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 following calculation rules:

String
  • char (n): n byte length
  • varchar (n): 2-byte string length storage, if it is utf-8, then the length 3n + 2
Numeric types
  • tinyint: 1 byte
  • smallint: 2 bytes
  • int: 4 bytes
  • bigint: 8 bytes
Time Type
  • date: 3 bytes
  • timestamp: 4 bytes
  • datetime: 8-byte field allows If NULL, the need to record is NULL byte 1

8 ref column

This column shows the index key columns of records in a table lookup column values or constants used in common are: const (constant), field names (example: film.id )

9 rows columns

This column is to be read and mysql estimate the number of rows of detection, note that this is not the number of rows in the result set

10 Extra column

This column shows additional information, refer to the following important common

  • Index the Using : Use a covering index

  • Where the Using : Use where statement to the results, the non-indexed columns covers the query

  • Index for condition Condition the Using : column of the query is not completely covered by the index, where conditions are a range of leading column.

  • The Temporary the Using : MySQL needs to create a temporary table to process the query, this situation is generally to be optimized, first thought of using the index to optimize.

    1. goods_name no index at this time to create a temporary table

    EXPLAIN SELECT DISTINCT goods_name from pea_goods

No indexing results

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% 相当于范围

Guess you like

Origin www.cnblogs.com/wxs121/p/12550107.html