MySQL查询性能优化-explain
查询优化
mysql的四种日志:
1.错误日志:Error Log.记录mysql运行过程ERROR,WARING等信息,系统出错或某条记录出问题可查看ERROR日志;
2.日常运行日志:General query log记录mysql中每条请求数据;
3.二进制日志:Binary log,包含一些事件,数据库的改动等;
4.慢查询日志:slow query log,用于mysql的性能调优;
慢查询:
1.开启慢查询,可以让mysql记录下查询超过指定时间的语句,通过定位分析性能的瓶颈,才能更好地优化数据库系统的性能。
slow_query_log 慢查询开启状态
slow_query_log_file 慢查询日志的存放位置
long_query_time查询超过多少秒才记录
2.可以通过设置全局变量的方法设定:
例如:set globle slow_query_long on
开启慢查询状态
service mysqld restart 即可
3.查询对应值:
show variables like'slow_query%';
show variables like 'long_query_time';
4.测试慢查询是否正确开启:
select sleep(2);
执行慢查询语句,查看是否有对应的慢查询日志生成。
如何优化数据库,如何提高数据库的性能?
-
索引
给数据库做索引,合理的索引能立即显著地提高数据库整个系统的性能。 -
存储过程
在适当的情况下,尽可能的用存储过程而不是SQL查询。因为前者已经过了预编译,运行速度更快。 -
优化查询语句
通过高性能的查询语句提高数据库的性能。
explain
Column | JSON Name | Meaning |
---|---|---|
id | select_id | The SELECT identifier |
select_type | None | The SELECT type |
table | table_name | The table for the output row |
partitions | partitions | The matching partitions |
type | access_type | The join type |
possible_keys | possible_keys | The possible indexes to choose |
key | key | The index actually chosen |
key_len | key_length | The length of the chosen key |
ref | ref | The columns compared to the index |
rows | rows | Estimate of rows to be examined |
filtered | filtered | Percentage of rows filtered by table condition |
Extra | None | Additional information |
id
表示查询中执行select子句或者操作表的顺序,id的值越大,代表优先级越高,越先执行
- id相同时,执行顺序由上至下
-
如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
- id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行
select_type
表示 select 查询的类型,主要是用于区分各种复杂的查询,例如:普通查询、联合查询、子查询等
- SIMPLE
(简单SELECT,不使用UNION或子查询等)
- PRIMARY
(查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY)
- UNION
如果union后边又出现的select 语句,则会被标记为union; 若 union 包含在 from 子句的子查询中,外层 select 将被标记为 derived。 (UNION中的第二个或后面的SELECT语句)
- DEPENDENT UNION
(UNION中的第二个或后面的SELECT语句,取决于外面的查询)
- UNION RESULT
(UNION的结果)
- SUBQUERY
(子查询中的第一个SELECT)
- DEPENDENT SUBQUERY
(子查询中的第一个SELECT,取决于外面的查询)
- DERIVED
(派生表的SELECT, FROM子句的子查询)
- UNCACHEABLE SUBQUERY
(一个子查询的结果不能被缓存,必须重新评估外链接的第一行)
table
查询的表名,并不一定是真实存在的表,有别名显示别名,也可能为临时表;
partitions
查询时匹配到的分区信息,对于非分区表值为NULL,当查询的是分区表时,partitions显示分区表命中的分区情况。
type
查询使用了何种类型,它在 SQL优化中是一个非常重要的指标, 以下性能从好到坏依次是:
system > const > eq_ref > ref > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
- NULL
MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成;
- system:
当表仅有一行记录时(系统表),数据量很少,往往不需要进行磁盘IO
- const:
当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量, system是const类型的特例,当查询的表只有一行的情况下,使用system
mysql> explain select * from project where id=1000;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | project | NULL | const | PRIMARY | PRIMARY | 8 | const | 1 | 100.00 | NULL |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
- eq_ref
类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配, 简单来说,就是多表连接中使用primary key或者 unique key作为关联条件
- ref
表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值; ref使用非唯一性索引,会找到很多个符合条件的行
mysql> select * from project p, application a where p.id=a.project_id and a.name='test_project1';
Empty set (0.00 sec)
mysql> explain select * from project p, application a where p.id=a.project_id and a.name='test_project1';
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------------+------+----------+-------+
| 1 | SIMPLE | a | NULL | ref | name | name | 194 | const | 1 | 100.00 | NULL |
| 1 | SIMPLE | p | NULL | eq_ref | PRIMARY | PRIMARY | 8 | t.a.project_id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------------+------+----------+-------+
2 rows in set, 1 warning (0.01 sec)
- ref_or_null
这种连接类型类似于 ref,区别在于搜索包含NULL值的行
- index_merge
使用了索引合并优化方法,查询使用了两个以上的索引。
- range
只检索给定范围的行,使用一个索引来选择行
- index
Full Index Scan,index与ALL区别为index类型只遍历索引树
mysql> explain select id from project;
+----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | project | NULL | index | NULL | name | 194 | NULL | 2 | 100.00 | Using index |
+----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
- ALL
Full Table Scan, MySQL将遍历全表以找到匹配的行
mysql> explain select * from project;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | project | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 100.00 | NULL |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
possible_keys
指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用
该列完全独立于EXPLAIN输出所示的表的次序。这意味着在possible_keys中的某些键实际上不能按生成的表次序使用
如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高你的查询性能。 如果是这样,创造一个适当的索引并且再次用EXPLAIN检查查询
mysql> explain select id, name from project where name like "test";
+----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | project | NULL | range | name | name | 194 | NULL | 1 | 100.00 | Using where; Using index |
+----+-------------+---------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
Key
区别于possible_keys,key是查询中实际使用到的索引,若没有使用索引,显示为NULL;
key列显示MySQL实际决定使用的键(索引)
如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引, 在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。
key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度
key_len显示的值为索引字段的最大可能长度,并非实际使用长度, 即key_len是根据表定义计算而得,不是通过表内检索出的
不损失精确性的情况下,长度越短越好
ref
常见的有:const,func,null,字段名;
当使用常量等值查询,显示const;
当关联查询时,会显示相应关联表的关联字段;
如果查询条件使用了表达式、函数,或者条件列发生内部隐式转换,可能显示为func
其他情况null
rows
根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数
这是评估SQL 性能的一个比较重要的数据,mysql需要扫描的行数, 很直观的显示 SQL 性能的好坏,一般情况下 rows 值越小越好。
filtered
这个是一个百分比的值,表里符合条件的记录数的百分比。
简单点说,这个字段表示存储引擎返回的数据在经过过滤后,剩下满足条件的记录数量的比例。
在MySQL.5.7版本以前想要显示filtered需要使用explain extended命令。 MySQL.5.7后,默认explain直接显示partitions和filtered的信息。
Extra
不适合在其他列中显示的信息,Explain 中的很多额外的信息会在 Extra 字段显示。
有以下几种情况:
- Using index
我们在相应的 select 操作中使用了覆盖索引,通俗一点讲就是查询的列被索引覆盖,使用到覆盖索引查询速度会非常快,SQl优化中理想的状态。
- Using where
列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的, 这发生在对表的全部的请求列都是同一个索引的部分的时候, 表示mysql服务器将在存储引擎检索行后再进行过滤
- Using temporary
表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询
- Using filesort
MySQL中无法利用索引完成的排序操作称为“文件排序”
- Using join buffer
在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。 如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。
- Impossible where
where语句会导致没有符合条件的行
- Select tables optimized away
仅通过使用索引,优化器可能仅从聚合函数结果中返回一行
expample:
explain select * from t where c=1;
+----+-------------+-------+-------------------------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-------------------------------+------+---------------+------+---------+-------+------+----------+-------+
| 1 | SIMPLE | t | p_2017,p_2018,p_2019,p_others | ref | c | c | 5 | const | 4 | 100.00 | NULL |
+----+-------------+-------+-------------------------------+------+---------------+------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
refs
https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
https://dev.mysql.com/doc/refman/8.0/en/explain-output.html