MySQL优化之索引优化-排序优化

54 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第13天,点击查看活动详情

前言

上篇我们学习了MySQL中的数据库优化之索引优化-子查询与GROUP BY优化。有兴趣的小伙伴可以阅读(MySQL优化之索引优化-子查询与GROUP BY优化)。
下面我们继续学习MySQL中的数据库优化之索引优化-排序优化。

排序优化

  1. SQL中,可以在WHERE子句和ORDER BY子句中使用索引,目的是在WHERE子句中避免全表扫描,在order by子句避免使用FileSort排序。当然,某些情况下全表扫描,或者FileSort排序不一定比索引慢。但是我们还是要避免,以提高索引效率。
  2. 尽量使用Index完成order by排序。如果WHERE子句和ORDER BY后面是相同的列就使用单索引列,如果不同就使用联合索引。
  3. 无法使用Index时,需要对FileSort方式进行调优。

举例

查询年龄30,且学生编号小于203000的学生,按用户名称排序。

EXPLAIN SELECT SQL_NO_CACHE *
FROM student
WHERE age = 30 AND stuno < 203000
ORDER BY NAME;

结果中可以看到type为ALL。运行查询语句,耗时3.16s。这是最坏的情况,Extra里还出现了Using filesort,也是最坏的情况。优化是必须的。

优化方案:

  1. 为了去掉filesort,我们创建索引。
CREATE INDEX idx_age_name ON student(age, NAME);
  1. 尽量让where过滤条件和排序使用上索引。
    建一个三个字段的索引:
DROP INDEX idx_age_name ON student;
CREATE INDEX idx_age_stuno_name ON student(age, stuno, NAME);

创建索引后运行查询语句,基本没有耗时。

总结

  1. 两个索引同时存在,mysql自动选择最优的方案。但是随着数据量的变化,选择的索引也会随之变化。
  2. 当范围条件和group by或者order by的字段出现二选一时,优先观察条件字段的过滤数量,如果过滤的数据足够多,而需要排序的数据并不多时,优先把索引放在范围字段上。反之,亦然。

filesort算法:双路排序和单路排序。

双路排序(慢)

  • MySQL4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和order by列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出。
  • 从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。

取一批数据,要对磁盘进行两次扫描,众所周知,IO是很耗时的,所以在mysql4.1出现之后,出现了第二种改进的算法,就是单路排序。

单路排序(快)

从磁盘读取查询需要的所有列,按照order by列在buffer对他们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存在内存中了。

结论和引申的问题

  • 由于单路是后出的,总体而言好过双路。
  • 但是用单路有问题。

优化策略

  1. 尝试提高sort_buffer=size
  2. 尝试提高max_length_for_sort_data
  3. order by时select *是一个大忌。最好只query需要的字段。

今天先学习到这里,明天继续。