Mysql序列(七)—— order by优化
前言
在mysql中满足order by的处理方式有两种:
- 让索引满足排序,即扫描有序索引然后再找到对应的行结果,这样结果即是有序;
- 使用索引查询出结果或者扫描表得到结果然后使用filesort排序;
filesort排序:是针结果在内存中进行排序,如果待排结果较大,mysql可能会产生临时文件输入输出到磁盘。
所以order by的优化思路
- 优先使用索引满足排序;
- 如果实在满足不了,再优化filesort排序;
使用索引满足order by规律总结
一般where子句的条件查询,大部分条件查询都会使用索引,如果这时加上order by索引生效,只要通过扫描索引即可完成排序,避免了额外的filesort的开销。
In some cases, MySQL may use an index to satisfy an ORDER BY clause and avoid the extra sorting involved in performing a filesort operation.
mysql官方手册描述:
在一些情况下,mysql可能会使用索引满足order by子句,从而避免了使用filesort操作进行的额外排序。
让order by使用索引进行排序时,并不一定需要order by子句中的列完全匹配索引,只要索引中未使用的部分和除了order by列的额外列都在where子句中作为常量匹配,order by仍然可能会使用索引实现排序。
在order by是否使用索引进行排序,都得依赖优化器决定使用索引是否比不使用索引更加高效
下面就总结下order by使用索引排序的几种情况:
- 如果select的结果是索引列的一部分(即覆盖索引),且order by按照索引的列进行排序,这种情况会扫描整个索引作为返回结果且都不用查询行,同时也使用索引进行排序。如:
create index idx_id_name on table_name (id, name);
// 因为id,name组成联合索引——已经有序,只要扫描整个idx_id_name,获取name即是有序
select name from table_name order by id;
但是这样就会使用文件排序:
// 虽然这里会使用覆盖索引,但是mysql会对查询出的结果做文件排序
// 因为扫描索引idx_id_name,得出的id不一定是按照name有序
// idx_id_name只能保证对id是有序
select id from table_name order by name;
- 按照完整索引排序,查询所有表行。如:
create index idx_name on table_name (name);
select * from order by name;
以上情况理论上按照使用索引实现排序。但是查询结果包含了其他的非索引列,所以优化器会抉择扫描整个有序索引然后再按需查找所有行是不是比扫描总个表使用filesort哪种方式更廉价。
- 使用索引一部分列作为where条件常量,另一个部分列作为order by排序,查询所有行。如:
// id,name组成联合索引,在id值为常量的索引中是按照name排序
create index idx_id_name on talbe_name (id, name);
select * from where id = constant order by name;
因为这里id是常量,所以按照where条件按照索引idx_id_name查找出的匹配结果行肯定在name上有序。这里影响是否使用索引满足排序的因素在于优化器判断按照id在扫描idx_id_name是比表扫描更廉价。
从以上的几种情况中可以看出两种影响是否使用索引完成排序的因素:
-
使用扫描索引找出匹配的结果行是否比全表扫描找出结果带来更大的性能收益。如果用来全表扫描,那么肯定不会使用filesort;
-
order by排序的列和where的条件的列索引使用上是否契合。如果按照where条件中索引匹配找到的结果,刚好就是按照order by排序的那么,索引查找过程也就完成了order by过程;如果不契合,即是使用了索引找到了匹配结果,任然需要filesort阶段完成排序。如果是单列索引,那么where需要和order by列上保持一致,如果是联合索引,where条件中使用索引列前缀部分,order by使用后半部分;
下面再看一些索引不满足order by的情况
- order by中使用了不同的索引进行排序
SELECT * FROM t1 ORDER BY key1, key2;
- 混合使用不同的顺序
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
- 用于检索结果行的索引和order by索引不是一个
SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
使用filesort满足order by
If an index cannot be used to satisfy an ORDER BY clause, MySQL performs a filesort operation that reads table rows and sorts them. A filesort constitutes an extra sorting phase in query execution.
如果索引不能满足order by,mysql将会执行filesort操作。filesort操作是读取表行,然后在排序它们。在查询中filesort构成了一个额外的排序阶段。
To obtain memory for filesort operations, the optimizer allocates a fixed amount of sort_buffer_size bytes up front. Individual sessions can change the session value of this variable as desired to avoid execessive memory use, or to allocate more memory as necessary.
为了能够执行filesort操作,优化器需要提前分配固定数量的内存,每个大小由sort_buffer_size参数决定。
A filesort operation uses temporary disk files as necessary if the result set is too large to fit in memory.
当结果集太大不能完全放入内存时,filesort操作会使用零时的磁盘文件进行换入换出。
filesort调优策略:
- 增大sort_buffer_size值,让其足够放下结果集,以便在内存中完成filesort;
- 增大read_rnd_buffer_size,以便更多行被一次读出;
判断order by使用了哪种方式
With EXPLAIN (see Section 8.8.1, “Optimizing Queries with EXPLAIN”), you can check whether MySQL can use indexes to resolve an ORDER BY clause:
- If the Extra column of EXPLAIN output does not contain Using filesort, the index is used and a filesort is not performed.
- If the Extra column of EXPLAIN output contains Using filesort, the index is not used and a filesort is performed.
使用explain,可以检测mysql使用了索引还是是用filesort完成order by:
- 如果explain的extra中有using filesort,那么表示使用了filesort;
- 如果没有filesort,表示使用了index;
总结
order by优化的步骤:
- 尽量让index方式完成order by:如何where和order by相同列则使用单索引列;如果不同使用联合索引;
- 无法使用时,对filesort方式进行调优;
参考
ORDER BY Optimization
Mysql order by与limit混用陷阱
mysql 大数据量分页优化