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

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

优化GROUP BY语句

默认情况下,MySQL对所有GROUP BY col1,col2...的字段进行排序。这与在查询中指定ORDER BY col1,col2...类似。因此,如果显式包括一个包含相同的列的ORDER BY子句,则对MySQL的实际执行性能没有什么影响。 如果查询包括GROUP BY 但用户想要避免排序结果的消耗,则可以指定ORDER By NULL禁止排序,例如:

Java代码   收藏代码
  1. explain select id, sum(moneys) from sales2 group by id \G     
  2. 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的字段都是升序或者都是降序。


例如:
Java代码   收藏代码
  1. SELECT * FROM t1 ORDER BY key_part1,key_part2,....:  
  2. SELECT * FROM t1 WHERE key_part1 = 1 ORDER BY key_part1 DESC,key_part2 DESC;  
  3. SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;  
但是以下的情况不使用索引:
Java代码   收藏代码
  1. SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;  
  2. --ORDER by的字段混合ASC 和 DESC  
  3. SELECT * FROM t1 WHERE key2=constant ORDER BY key1;  
  4.  ----用于查询行的关键字与ORDER BY 中所使用的不相同  
  5.  SELECT * FROM t1 ORDER BY key1, key2;  
  6.  ----对不同的关键字使用ORDER BY  
 
Java代码   收藏代码
  1.  mysql > explain 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 B . id ;  
  2. +----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+  
  3. | id | select_type | table | type    | possible_keys | key      | key_len | ref                      | rows   | Extra                            |  
  4. +----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+  
  5. |  1 | SIMPLE       | A      | ALL     | NULL           | NULL     | NULL     | NULL                     | 46585 | Using temporary ; Using filesort |  
  6. |  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        | joomla_test . A . catid      |      1 |                                 |  
  7. |  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY | 4        | joomla_test . A . sectionid |      1 | Using index                      |  
  8. +----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+  
  9. 3 rows in set ( 0.00 sec )  
  10.    
  11. mysql > explain 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 A . id ;  
  12. +----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+  
  13. | id | select_type | table | type    | possible_keys | key      | key_len | ref                      | rows   | Extra           |  
  14. +----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+  
  15. |  1 | SIMPLE       | A      | ALL     | NULL           | NULL     | NULL     | NULL                     | 46585 | Using filesort |  
  16. |  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        | joomla_test . A . catid      |      1 |                |  
  17. |  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY | 4        | joomla_test . A . sectionid |      1 | Using index     |  
  18. +----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+  
 对于上面两条语句,只是修改了一下排序字段,而第一个使用了Using temporary,而第二个却没有。在日常的网站维护中,如果有Using temporary出现,说明需要做一些优化措施了。

而为什么第一个用了临时表,而第二个没有用呢?
因为如果有ORDER BY子句和一个不同的GROUP BY子句,或者如果ORDER BY或GROUP BY中的字段都来自其他的表而非连接顺序中的第一个表的话,就会创建一个临时表了。
那么,对于上面例子中的第一条语句,我们需要对jos_categories的id进行排序,可以将SQL做如下改动:

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

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

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

Java代码   收藏代码
  1. 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 ;  
  2. +----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+  
  3. | id | select_type | table | type    | possible_keys          | key          | key_len | ref                  | rows   | Extra        |  
  4. +----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+  
  5. |  1 | SIMPLE       | C      | index   | PRIMARY                | PRIMARY      | 4        | NULL                 |      1 | Using index |  
  6. |  1 | SIMPLE       | A      | ref     | idx_catid , idx_section | idx_section | 4        | joomla_test . C . id     | 23293 | Using where |  
  7. |  1 | SIMPLE       | B      | eq_ref | PRIMARY                | PRIMARY      | 4        | joomla_test . A . catid |      1 | Using where |  
  8. +----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+  
  9. 3 rows in set ( 0.00 sec )  

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

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

Java代码   收藏代码
  1. mysql > explain 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 where A . id < 100 ;  
  2. +----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+  
  3. | id | select_type | table | type    | possible_keys   | key      | key_len | ref                      | rows | Extra        |  
  4. +----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+  
  5. |  1 | SIMPLE       | A      | range   | PRIMARY         | PRIMARY | 4        | NULL                     |    90 | Using where |  
  6. |  1 | SIMPLE       | B      | eq_ref | PRIMARY         | PRIMARY | 4        | joomla_test . A . catid      |    1 |             |  
  7. |  1 | SIMPLE       | C      | eq_ref | PRIMARY         | PRIMARY | 4        | joomla_test . A . sectionid |    1 | Using index |  
  8. +----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+  
  9. 3 rows in set ( 0.05 sec )  

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

Java代码   收藏代码
  1. mysql > explain 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 where A . id < 100 order by A . id ;  
  2. +----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+  
  3. | id | select_type | table | type    | possible_keys | key      | key_len | ref                      | rows | Extra        |  
  4. +----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+  
  5. |  1 | SIMPLE       | A      | range   | PRIMARY        | PRIMARY | 4        | NULL                     |  105 | Using where |  
  6. |  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        | joomla_test . A . catid      |    1 |             |  
  7. |  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY | 4        | joomla_test . A . sectionid |    1 | Using index |  
  8. +----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+  
  9. 3 rows in set ( 0.00 sec )  

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

Java代码   收藏代码
  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 left join   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的执行效率,并进行定期优化。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
25天前
|
SQL 存储 关系型数据库
一文搞懂SQL优化——如何高效添加数据
**SQL优化关键点:** 1. **批量插入**提高效率,一次性建议不超过500条。 2. **手动事务**减少开销,多条插入语句用一个事务。 3. **主键顺序插入**避免页分裂,提升性能。 4. **使用`LOAD DATA INFILE`**大批量导入快速。 5. **避免主键乱序**,减少不必要的磁盘操作。 6. **选择合适主键类型**,避免UUID或长主键导致的性能问题。 7. **避免主键修改**,保持索引稳定。 这些技巧能优化数据库操作,提升系统性能。
220 4
一文搞懂SQL优化——如何高效添加数据
|
1月前
|
SQL 存储 数据库连接
日活3kw下,如何应对实际业务场景中SQL过慢的优化挑战?
在面试中,SQL调优是一个常见的问题,通过这个问题可以考察应聘者对于提升SQL性能的理解和掌握程度。通常来说,SQL调优需要按照以下步骤展开。
|
1月前
|
SQL 关系型数据库 MySQL
【MySQL】— —熟练掌握用SQL语句实现数据库和基本表的创建。熟练掌握MySQL的安装、客户端登录方法;熟练掌握MySQL的编码、数据类型等基础知识;掌握实体完整性的定义和维护方法、掌握参照完整性
【MySQL】— —熟练掌握用SQL语句实现数据库和基本表的创建。熟练掌握MySQL的安装、客户端登录方法;熟练掌握MySQL的编码、数据类型等基础知识;掌握实体完整性的定义和维护方法、掌握参照完整性
101 1
|
1月前
|
存储 关系型数据库 MySQL
最全MySQL面试60题(含答案):存储引擎+数据库锁+索引+SQL优化等
最全MySQL面试60题(含答案):存储引擎+数据库锁+索引+SQL优化等
172 0
|
30天前
|
SQL 存储 BI
sql server 2012远程链接的方法及步骤
sql server 2012远程链接的方法及步骤
17 1
|
2天前
|
SQL 分布式计算 资源调度
一文解析 ODPS SQL 任务优化方法原理
本文重点尝试从ODPS SQL的逻辑执行计划和Logview中的执行计划出发,分析日常数据研发过程中各种优化方法背后的原理,覆盖了部分调优方法的分析,从知道怎么优化,到为什么这样优化,以及还能怎样优化。
|
5天前
|
SQL XML 前端开发
sql 性能优化基于explain调优(二)
sql 性能优化基于explain调优(二)
12 0
|
9天前
|
SQL 关系型数据库 数据库
【后端面经】【数据库与MySQL】SQL优化:如何发现SQL中的问题?
【4月更文挑战第12天】数据库优化涉及硬件升级、操作系统调整、服务器/引擎优化和SQL优化。SQL优化目标是减少磁盘IO和内存/CPU消耗。`EXPLAIN`命令用于检查SQL执行计划,关注`type`、`possible_keys`、`key`、`rows`和`filtered`字段。设计索引时考虑外键、频繁出现在`where`、`order by`和关联查询中的列,以及区分度高的列。大数据表改结构需谨慎,可能需要停机、低峰期变更或新建表。面试中应准备SQL优化案例,如覆盖索引、优化`order by`、`count`和索引提示。优化分页查询时避免大偏移量,可利用上一批的最大ID进行限制。
34 3
|
26天前
|
SQL 关系型数据库 MySQL
【MySQL技术之旅】(7)总结和盘点优化方案系列之常用SQL的优化
【MySQL技术之旅】(7)总结和盘点优化方案系列之常用SQL的优化
39 1
|
27天前
|
SQL 索引
SQL怎么优化
SQL怎么优化
30 2