如果文章中有不准确的地方,欢迎留言指正。
排序通常不是 SQL 里最先被注意到的问题,但一旦数据量上来,它往往会直接决定查询是否还能接受。理解 MySQL 的排序路径,有助于判断一次查询慢在什么地方,也更容易知道优化该从哪里下手。
MySQL 的两条排序路径#
索引排序#
索引排序顾名思义,就是直接利用索引中的有序性完成排序。
当查询条件和排序字段能够命中合适的索引时,MySQL 可以避免额外排序动作,直接按索引顺序返回结果。
索引排序的效率通常最高,但也有明显前提:排序字段必须与索引设计匹配。
一旦排序字段不满足索引顺序要求,MySQL 就只能退回到 filesort。
Filesort#
执行 EXPLAIN 时,如果 Extra 字段里出现 Using filesort,表示 MySQL 无法直接按索引顺序返回结果,需要额外做排序。
这里有个很容易误解的点:Using filesort 不等于一定落盘。
它表示“额外排序”这件事发生了,至于是用内存完成还是需要借助磁盘,取决于数据量和缓冲区大小。
按照排序位置,大致可以分成两类:
- 内存排序:待排序数据可以放进
sort_buffer时,直接在内存中完成排序。sort_buffer_size可以调整这块缓冲区大小。 - 磁盘排序:数据量超出内存可承载范围时,会拆分成多块排序,再走归并流程,I/O 成本明显更高。
按照数据组织方式,filesort 又可以分成单路排序(Single-Pass Sort)和双路排序(Two-Pass Sort):
- 单路排序:将
select需要返回的字段一并放入排序缓冲区,排完序后直接返回结果。 - 双路排序:先对主键和排序字段排序,再根据主键回表取出其余字段。
以上,是 8.0.20 之前的策略。在之后的版本中,MySQL内部会根据数据的大小和内存使用情况自动选择最优化的排序策略,而不再依赖 max_length_for_sort_data 参数。
排序优化时优先看什么#
从性能上看,一般可以理解为:
索引排序 > 内存中的 filesort > 磁盘 filesort
所以排查排序慢查询时,我会优先看这几件事:
- 能不能通过索引顺序直接完成排序
Using filesort是不是 unavoidableselect字段是否过多,导致更容易走双路排序sort_buffer_size是否过小,导致内存装不下
总结#
理解 MySQL 排序机制时,最重要的不是记住术语,而是建立一个判断顺序:
- 先看能不能走索引排序
- 再看
Using filesort是不是必须接受 - 最后再考虑通过缓冲区和字段裁剪去减少排序成本
真正有价值的优化,往往不是一味调参数,而是先让 MySQL 少做一次本可以避免的排序。
