开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第13天,点击查看活动详情
前言
上篇我们学习了MySQL中的数据库优化之索引优化-子查询与GROUP BY优化。有兴趣的小伙伴可以阅读(MySQL优化之索引优化-子查询与GROUP BY优化)。
下面我们继续学习MySQL中的数据库优化之索引优化-排序优化。
排序优化
- SQL中,可以在WHERE子句和ORDER BY子句中使用索引,目的是在WHERE子句中避免全表扫描,在order by子句避免使用FileSort排序。当然,某些情况下全表扫描,或者FileSort排序不一定比索引慢。但是我们还是要避免,以提高索引效率。
- 尽量使用Index完成order by排序。如果WHERE子句和ORDER BY后面是相同的列就使用单索引列,如果不同就使用联合索引。
- 无法使用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,也是最坏的情况。优化是必须的。
优化方案:
- 为了去掉filesort,我们创建索引。
CREATE INDEX idx_age_name ON student(age, NAME);
- 尽量让where过滤条件和排序使用上索引。
建一个三个字段的索引:
DROP INDEX idx_age_name ON student;
CREATE INDEX idx_age_stuno_name ON student(age, stuno, NAME);
创建索引后运行查询语句,基本没有耗时。
总结
- 两个索引同时存在,mysql自动选择最优的方案。但是随着数据量的变化,选择的索引也会随之变化。
- 当范围条件和group by或者order by的字段出现二选一时,优先观察条件字段的过滤数量,如果过滤的数据足够多,而需要排序的数据并不多时,优先把索引放在范围字段上。反之,亦然。
filesort算法:双路排序和单路排序。
双路排序(慢)
- MySQL4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和order by列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出。
- 从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。
取一批数据,要对磁盘进行两次扫描,众所周知,IO是很耗时的,所以在mysql4.1出现之后,出现了第二种改进的算法,就是单路排序。
单路排序(快)
从磁盘读取查询需要的所有列,按照order by列在buffer对他们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存在内存中了。
结论和引申的问题
- 由于单路是后出的,总体而言好过双路。
- 但是用单路有问题。
优化策略
- 尝试提高sort_buffer=size
- 尝试提高max_length_for_sort_data
- order by时select *是一个大忌。最好只query需要的字段。
今天先学习到这里,明天继续。