目录
产生
分类对比
产生情况
进行优化
-
产生
- 如果索引不能用于满足ORDER BY子句,MySQL将执行filesort读取表行并对它们进行排序的操作
- 用Explain语句可以看到最后一列显示using filesort
- EXPLAIN展示的执行计划的Extra这个字段中的“Using filesort”表示的就是需要额外的排序操作
- MySQL会给每个线程分配一块内存用于排序,称为sort_buffer
- 这里的filesort有可能是内存排序,也有可能是文件排序,但它们都统称filesort
- 在内存中对数据进行排序,如果sort_buffer_size超过了需要排序的数据量的大小,表示排序可以直接在内存中完成
- 那么文件排序又是什么意思呢?
- 实际上如果需要排序的数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序
- 文件排序就是所谓的外部排序
- 所以说,MySQL的Order By的实现,就有可能利用到外部排序这种排序算法
- 外部文件排序一般使用归并排序算法
- MySQL将需要排序的数据分成N份,每一份单独排序后存在这些临时文件中,然后把这N个有序文件再一步步的合并成一个有序的大文件
-
分类对比
- filesort文件排序方式分为
- 单路排序(全字段排序):
- 是一次性取出满足条件行的所有字段,然后在sort buffer中进行排序
- 举个例子,下面有一段sql:
- 单路排序过程:
- 从索引name找到第一个满足 name = ‘blblccc’ 条件的主键 id
- 根据主键 id 取出整行,取出所有字段的值,存入sort_buffer(排序缓存)中
- 从索引name找到下一个满足 name = ‘blblccc’ 条件的主键 id
- 重复步骤 2、3 直到不满足 name = ‘blblccc’
- 对 sort_buffer 中的数据按照字段 age 进行排序
- 返回结果给客户端
- 双路排序(又叫回表排序模式,rowid排序):
- 是首先根据相应的条件取出相应的 排序字段 和可以直接定位行数据的行 ID,然后在 sort buffer 中进行排序,排序完后需要再次取回其它需要的字段
- 双路排序过程:
- 从索引 name 找到第一个满足 name = ‘blblccc’ 的主键id
- 根据主键 id 取出整行,把排序字段 age 和主键 id 这两个字段放到sort buffer(排序缓存)中
- 从索引 name 取下一个满足 name = ‘blblccc’ 记录的主键 id
- 重复 3、4 直到不满足 name = ‘blblccc’
- 对 sort_buffer 中的字段 age 和主键 id 按照字段 age进行排序
- 遍历排序好的 id 和字段 age,按照 id 的值回到原表中取出所有字段的值返回给客户端
- 对比两个排序模式:
- 单路排序会把所有需要查询的字段都放到 sort buffer 中
- 而双路排序只会把主键和需要排序的字段放到 sort buffer 中进行排序,然后再通过主键回到原表查询需要的字段
- 单路排序占用内存大,但效率高,因为排好序后就已经是可以返回的结果了
- 双路排序占用内存少,但效率要比单路排序低,因为排好序后还要回表加载数据
-
产生情况
- order by 字段不是索引字段
- order by 字段是索引字段,但是 select 中没有使用覆盖索引,如:select * from staffs order by age asc;
- order by 中同时存在 ASC 升序排序和 DESC 降序排序,如:select a, b from staffs order by a desc, b asc;
- order by 多个字段排序时,不是按照索引顺序进行 order by,即不是按照最左前缀法则,如:select a, b from staffs order by b asc, a asc;
-
进行优化
- 什么时候使用单路/双路关键在于:
- sort_buffer可供排序的内存缓冲区大小
- max_length_for_sort_data 单行所有字段总和限制,超过这个大小启动双路排序
- mysql内部是通过比较 max_length_for_sort_data(默认1024字节)这个系统变量跟需要排序的字段长度比较来判断采用哪种排序的
- 如果需要排序的字段总长度小于max_length_for_sort_data,则采用单路排序
- 如果需要排序的字段总长度大于max_length_for_sort_data,则采用双路排序
- 如果 MySQL 排序内存 sort_buffer 配置的比较小并且没有条件继续增加了,可以适当把 max_length_for_sort_data 配置小点,让优化器选择使用 双路排序 算法,可以在 sort_buffer 中一次排序更多的行,只是需要再根据主键回到原表取数据
- 如果 MySQL 排序内存有条件可以配置比较大,可以适当增大 max_length_for_sort_data 的值,让优化器优先选择单路排序,把需要的字段放到 sort_buffer 中,这样排序后就会直接从内存里返回查询结果了
- 所以,MySQL通过 max_length_for_sort_data 和 sort_buffer 参数来控制排序,在不同场景使用不同的排序模式,从而提升排序效率
- 注意,如果全部使用sort_buffer内存排序一般情况下效率会高于磁盘文件排序,但不能因为这个就随便增加