MySQL排序优化详细解析
作者:智由静生
MySQL排序优化
MySQL有两种方式生成有序的结果:
1.通过排序操作;
2.按索引顺序扫描。如果EXPLAIN出来的type列的值为“index”,则说明使用了索引扫描来做排序。(但如果不为“index”,也不能说明没有使用索引扫描来做排序。)
一、使用索引生成排序
MySQL可以使用同一个索引既满足排序(ORDER BY),又用于查询(WHERE)操作的。因此如果可以,设计索引的时候尽可能考虑同时满足两种任务。
索引扫描本身是很快的,但如果索引不能覆盖查询所需的全部列,那就不得不每扫描一条索引记录就回表查询一次对应的行。这基本都是随机I/O,因此按索引顺序读取数据的速度通常要比顺序全表扫描慢。索引覆盖还有个额外的红利,就是主键。即使索引列中不包含主键,但因为二级索引的叶子节点包含了主键的值,所以也能用于对主键做覆盖查询。
只有当索引的列顺序和ORDER BY子句的顺序完全一致,并且所有列的排序方向都一样,MySQL才能使用索引对结果进行排序。如果查询关联多张表,则只有当ORDER BY子句引用的字段全部为第一张表时,才能使用索引做排序。
ORDER BY子句使用索引的限制和where查询是一样的,都需要满足左前缀要求。但有一种情况ORDER BY子句可以不满足左前缀要求。如果where子句或者join子句中对相关列指定了常量,就可以弥补索引的不足。
例如,有一张租赁表rental在列 (rental_data, inventory_id, customer_id) 上有索引。可以使用该索引为以下查询做排序。EXPLAIN中可以看到没有出现文件排序 (filesort)。
即使ORDER BY子句不满足索引的最左前缀的要求,也可以用于查询排序,这是因为索引的第一列被指定为一个常数。这个查询在不同版本的MySQL中可能会有不同的表现。因为select列中的staff_id不在排序索引中,而且不是主键,没有实现索引覆盖,因此按索引顺序读取数据的速度通常要比顺序全表扫描慢,有些版本的MySQL可能会通过计算成本选择文件排序 (filesort)。如果只有rental_id列就没问题了,前面说过主键有额外的“红利”。
下面是一些不能使用索引做排序的例子:
--排序方向与索引列不一致 WHERE rental_date='1999-05-05' ORDER BY inventory_id DESC,customer_id ASC
--排序字段包含一个不在索引中的字段 WHERE rental_date='1999-05-05' ORDER BY inventory_id ,staff_id
--排序不满足最左法则 WHERE rental_date='1999-05-05' ORDER BY customer_id
--rental_date使用了范围查询,后续索引列失效 WHERE rental_date>'1999-05-05' ORDER BY inventory_id,customer_id
#inventory_id 使用了in多个等值条件查询,in对于排序认为是一种范围查询 WHERE rental_date='1999-05-05' AND inventory_id in (xx,xx) ORDER BY customer_id
下面的例子理论上是可以使用索引进行关联排序的,但由于优化器在优化时将film_actor表当作关联的第二张表,所以实际无法使用索引排序。
应该尽可能使用更多的索引列。索引只能使用索引的最左前缀,当遇到第一个范围查询,就会停止使用后面的索引列。MYSQL无法再使用范围列后面的其他字段进行排序了,但对于“多个等值条件查询”则没有限制,所以可以考虑把范围查询语句改写成IN列表的形式。但是IN在where条件中不算范围查询,但对于order by不适用,对于排序认为是一种范围查询。在前面举的不能使用索引做排序的例子的最后一个就说明了这个问题。
如果排序查询中有LIMIT,那么LIMIT也会在排序之后应用,所以即使需要返回较少的数据,需要排序的数据量仍然非常大(虽然MySQL后续版本做了优化,根据实际情况抛弃不满足条件的结果,然后再排序)。所以此时如果能使用索引排序是最好的选择。
如果服务器能够按需要顺序读取数据,那么就不再需要额外的排序操作,并且group by查询也无需再做排序和将行按组进行聚合计算了。
二、文件排序
当不能使用索引生成排序结果时,需要进行文件排序(filesort),如果数据量小则在内存中进行,如果数据量大则需要使用磁盘,这两种情况都叫文件排序(filesort)。如果需要排序的数据量小于“排序缓冲区”,就使用内存“快速排序”。如果内存不够,就先将数据分块,对每个独立的块使用“快速排序”,并将各个块的排序结果存放正在磁盘上,然后将各个块进行合并(mergge),最后返回排序结果。
在关联查询的时候如果需要排序,MySQL会分两种情况处理这样的文件排序。如果ORDER BY子句的所有列都来自关联的第一张表,那么MySQL在关联处理第一个表的时候就进行文件排序。如果是这样,那么在MySQL的EXPLAIN结果中可以看到Extra字段会有“Using filesort”。除此以外的所有情况,MySQL都会先将关联的结果存放到一个临时表中,然后在所有关联都结束后,再进行文件排序。这种情况下,在MySQL的EXPLAIN结果中的Extra字段可以看到“Using temporary; Using filesort”。
所以尽量使ORDER BY子句的所有列都来自关联的第一张表。
到此这篇关于MySQL排序优化详细解析的文章就介绍到这了,更多相关MySQL排序优化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!