EXPLAIN sql优化方法(2) Using temporary ; Using filesort

优化GROUP BY语句
   默认情况下,MySQL对所有GROUP BY col1,col2...的字段进行排序。这与在查询中指定ORDER BY col1,col2...类似。因此,如果显式包括一个包含相同的列的ORDER BY子句,则对MySQL的实际执行性能没有什么影响。 如果查询包括GROUP BY 但用户想要避免排序结果的消耗,则可以指定ORDER By NULL禁止排序,例如:
explain select id, sum(moneys) from sales2 group by id \G
explain select id, sum(moneys) from sales2 group by id order by null \G
你可以通过比较发现第一条语句会比第二句在Extra:里面多了Using filesort.而恰恰filesort是最耗时的。

优化ORDER BY语句
    在某些情况中,MySQL可以使用一个索引来满足ORDER BY子句,而不需要额外的排序。WHERE 条件和 ORDER BY使用相同的索引,并且ORDER BY的顺序和索引顺序相同,并且ORDER BY的字段都是升序或者都是降序。
例如:
SELECT * FROM t1 ORDER BY key_part1,key_part2,....:
SELECT * FROM t1 WHERE key_part1 = 1 ORDER BY key_part1 DESC,key_part2 DESC;
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;
但是以下的情况不使用索引:
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
--ORDER by的字段混合ASC 和 DESC
SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
----用于查询行的关键字与ORDER BY 中所使用的不相同
SELECT * FROM t1 ORDER BY key1, key2;
----对不同的关键字使用ORDER BY

mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categoriesB on A . catid = B . id left join jos_sections C on A . sectionid = C . id order by B . id ;
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+
| id | select_type | table | type    | possible_keys | key      | key_len | ref                      | rows   | Extra                            |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+
|  1 | SIMPLE       | A      | ALL     | NULL           | NULL     | NULL     |NULL                     | 46585 | Using temporary ; Using filesort |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . catid      |      1 |                                 |
|  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . sectionid |      1 | Using index                      |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+
3 rows in set ( 0.00 sec ) 
 
mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categoriesB on A . catid = B . id left join jos_sections C on A . sectionid = C . id order by A . id ;
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+
| id | select_type | table | type    | possible_keys | key      | key_len | ref                      | rows   | Extra           |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+
|  1 | SIMPLE       | A      | ALL     | NULL           | NULL     | NULL     |NULL                     | 46585 | Using filesort |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . catid      |      1 |                |
|  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . sectionid |      1 | Using index     |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+

对于上面两条语句,只是修改了一下排序字段,而第一个使用了Using temporary,而第二个却没有。在日常的网站维护中,如果有Using temporary出现,说明需要做一些优化措施了。
而为什么第一个用了临时表,而第二个没有用呢?
因为如果有ORDER BY子句和一个不同的GROUP BY子句,或者如果ORDER BY或GROUP BY中的字段都来自其他的表而非连接顺序中的第一个表的话,就会创建一个临时表了。
那么,对于上面例子中的第一条语句,我们需要对jos_categories的id进行排序,可以将SQL做如下改动:

mysql > explain select B . id , B . title , A . title from jos_categories A left join jos_contentB on A . id = B . catid left join jos_sections C on B . sectionid = C . id order by A . id ;
+----+-------------+-------+--------+---------------+-----------+---------+-------------------------+------+----------------+
| id | select_type | table | type    | possible_keys | key        | key_len | ref                      | rows | Extra           |
+----+-------------+-------+--------+---------------+-----------+---------+-------------------------+------+----------------+
|  1 | SIMPLE       | A      | ALL     | NULL           | NULL       | NULL    | NULL                     |    18 | Using filesort |
|  1 | SIMPLE       | B      | ref     | idx_catid      | idx_catid | 4        |joomla_test . A . id         | 3328 |                |
|  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY    | 4        |joomla_test . B . sectionid |    1 | Using index     |
+----+-------------+-------+--------+---------------+-----------+---------+-------------------------+------+----------------+
3 rows in set ( 0.00 sec )

这样我们发现,不会再有Using temporary了,而且在查询jos_content时,查询的记录明显有了数量级的降低,这是因为jos_content的idx_catid起了作用。
所以结论是:

尽量对第一个表的索引键进行排序,这样效率是高的。
我们还会发现,在排序的语句中都出现了Using filesort,字面意思可能会被理解为:使用文件进行排序或中文件中进行排序。实际上这是不正确的,这是一个让人产生误解的词语。
当我们试图对一个没有索引的字段进行排序时,就是filesoft。它跟文件没有任何关系,实际上是内部的一个快速排序。
然而,当我们回过头来再看上面运行过的一个SQL的时候会有以下发现:

mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B ,jos_sections C where A . catid = B . id and A . sectionid = C . id order by C . id ;
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
| id | select_type | table | type    | possible_keys          | key          | key_len| ref                  | rows   | Extra        |
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
|  1 | SIMPLE       | C      | index   | PRIMARY                | PRIMARY      | 4        | NULL                 |      1 | Using index |
|  1 | SIMPLE       | A      | ref     | idx_catid , idx_section | idx_section | 4       | joomla_test . C . id     | 23293 | Using where |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY                | PRIMARY      |4        | joomla_test . A . catid |      1 | Using where |
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
3 rows in set ( 0.00 sec )

这是我们刚才运行过的一条语句,只是加了一个排序,而这条语句中C表的主键对排序起了作用,我们会发现Using filesort没有了。
而尽管在上面的语句中也是对第一个表的主键进行排序,却没有得到想要的效果(第一个表的主键没有用到),这是为什么呢?实际上以上运行过的所有left join的语句中,第一个表的索引都没有用到,尽管对第一个表的主键进行了排序也无济于事。不免有些奇怪!

于是我们继续测试了下一条SQL:

mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categoriesB on A . catid = B . id left join jos_sections C on A . sectionid = C . id where A . id < 100 ;
+----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+
| id | select_type | table | type    | possible_keys   | key      | key_len | ref                      | rows | Extra        |
+----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+
|  1 | SIMPLE       | A      | range   | PRIMARY         | PRIMARY | 4        |NULL                     |    90 | Using where |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY         | PRIMARY | 4        |joomla_test . A . catid      |    1 |             |
|  1 | SIMPLE       | C      | eq_ref | PRIMARY         | PRIMARY | 4        |joomla_test . A . sectionid |    1 | Using index |
+----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+
3 rows in set ( 0.05 sec )

然后,当再次进行排序操作的时候,Using filesoft也没有再出现

mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categoriesB on A . catid = B . id left join jos_sections C on A . sectionid = C . id where A . id < 100order by A . id ;
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+
| id | select_type | table | type    | possible_keys | key      | key_len | ref                      | rows | Extra        |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+
|  1 | SIMPLE       | A      | range   | PRIMARY        | PRIMARY | 4        |NULL                     |  105 | Using where |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . catid      |    1 |             |
|  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . sectionid |    1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+
3 rows in set ( 0.00 sec )

这个结果表明:对where条件里涉及到的字段,Mysql会使用索引进行搜索,而这个索引的使用也对排序的效率有很好的提升。
写了段程序测试了一下,分别让以下两个SQL语句执行200次:

  1. select A . id , A . title , B . title from jos_content   A left join jos_categories B on A .catid = B . id left join jos_sections C   on A . sectionid = C . id
  2. select   A . id , A . title , B . title from jos_content   A , jos_categories B ,jos_sections C where A . catid = B . id and   A . sectionid = C . id
  3. select   A . id , A . title , B . title from jos_content A left   join jos_categories B on A. catid = B . id left join jos_sections C on   A . sectionid = C . id   order by rand ()limit 10
  4. select   A . id from   jos_content A left join jos_categories B on B . id = A . catid leftjoin   jos_sections C on A . sectionid = C . id order by A . id

结果是第(1)条平均用时20s ,第(2)条平均用时44s ,第(3)条平均用时70s ,第(4)条平均用时2s 。而且假如我们用explain观察第(3)条语句的执行情况,会发现它创建了temporary表来进行排序。

综上所述,可以得出如下结论:
1. 对需要查询和排序的字段要加索引。
2. 在一定环境下,left join还是比普通连接查询效率要高,但是要尽量少地连接表,并且在做连接查询时注意观察索引是否起了作用。
3. 排序尽量对第一个表的索引字段进行,可以避免mysql创建临时表,这是非常耗资源的。
4. 对where条件里涉及到的字段,应适当地添加索引,这样会对排序操作有优化的作用。
5. 在做随机抽取数据的需求时,避免使用order by rand(),从上面的例子可以看出,这种是很浪费数据库资源的,在执行过程中用show processlist查看,会发现第(3)条有Copying to tmp table on disk。而对(3)和(4)的对比得知,如果要实现这个功能,最好另辟奚径,来减轻Mysql的压力。
6. 从第4点可以看出,如果说在分页时我们能先得到主键,再根据主键查询相关内容,也能得到查询的优化效果。通过国外《High Performance MySQL》专家组的测试可以看出,根据主键进行查询的类似“SELECT ... FROM... WHERE id = ...”的SQL语句(其中id为PRIMARYKEY),每秒钟能够处理10000次 以上的查询,而普通的SELECT查询每秒只能处理几十次到几百次 。涉及到分页的查询效率问题,网上的可用资源越来越多,查询功能也体现出了它的重要性。也便是sphinx、lucene这些第三方搜索引擎的用武之地了。
7. 在平时的作业中,可以打开Mysql的Slow queries功能,经常检查一下是哪些语句降低的Mysql的执行效率,并进行定期优化。

时间: 2024-11-07 12:11:08

EXPLAIN sql优化方法(2) Using temporary ; Using filesort的相关文章

sql优化方法

1. SELECT子句中避免使用 “*” 当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用‘*’是一个方便的方法.不幸的是,这是一个非常低效的方法. 实际上,ORACLE在解析的过程中, 会将“*” 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间. 2.使用DECODE函数来减少处理时间 使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表. 例如: Sql代码 SELECT COUNT(*),SUM(SAL) FROM E

sql 优化方法

(1)Where子句中:where表之间的连接必须写在其他Where条件之前,那些可以过滤掉最大数量记录的条件必须写在Where子句的末尾.HAVING最后.---  需要按照顺序进行一级一级的筛选 (2)用EXISTS替代IN.用NOT EXISTS替代NOT IN. exists 条件 返回的 是boolean型  只有 true 或者 false; 所以在exists 中的id 条件 应该是 外查询的查询id SELECT * FROM alumni_info t WHERE EXISTS

SQL优化方法以及案例

http://weibo.com/p/23041861d758500102w331 http://blog.jobbole.com/87450/

转://从一条巨慢SQL看基于Oracle的SQL优化

http://mp.weixin.qq.com/s/DkIPwbDKIjH2FMN13GkT4w 本次分享的内容是基于Oracle的SQL优化,以一条巨慢的SQL为例,从快速解读SQL执行计划.如何从执行计划中找到SQL执行慢的Root Cause.统计信息与cardinality问题.探索性能杀手Filter操作.如何进行逻辑重写让SQL起飞等多个维度进行解析,最终优化巨慢SQL语句,希望能够抛砖引玉,和大家一起探讨SQL优化方法. 另外,还简单介绍了两种解决疑难SQL优化问题的工具:1005

SQL优化技巧(Oracle)

SQL优化技巧(1): Where子句中的连接顺序:oracle采用自下而 上的顺序解析where子句,根据这个原理,表 之间的连接必须写在其他where条件之前,那些可以过滤掉大量记录的条件 必须写在where子句的末尾. 例如 低效:select * from report_sale_account e where hsje>5000 and dzxl = '000001' and 25<(select count(*) from report_sale_account where cod

30多条mysql数据库优化方法,千万级数据库记录查询轻松解决【转】

转自:http://www.ihref.com/read-16422.html 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描, Sql 代码 : select id from t where num is null; 可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询: Sql 代码 : s

基于oracle的sql优化方法论

Oracle数据库里SQL优化的终极目标就是要缩短目标SQL语句的执行时间.要达到上述目的,我们通常只有如下三种方法可以选择: 1.降低目标SQL语句的资源消耗: 2.并行执行目标SQL语句: 3.平衡系统的资源消耗. "方法1:降低目标SQL语句的资源消耗"以缩短执行时间,这是最常用的SQL优化方法.这种方法的核心是要么通过在不更改业务逻辑的情况下改写SQL来降低目标SQL语句的资源消耗,要么不改SQL但通过调整执行计划或相关表的数据来降低目标SQL语句的资源消耗. 方法2:并行执行

转载:30多条mysql数据库优化方法,千万级数据库记录查询轻松解决

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描, Sql 代码 : select id from t where num is null; 可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询: Sql 代码 : select id from t where num=0; 3.应尽量避免在 wh

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

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