MySql的filesort的优化

781 阅读4分钟
MySql
filesort
的优化
有时我们使用
EXPLAIN
工具,可以看到查询计划的输出中的
Extra
列有
filesort
filesort
往往意味着你没有利用到索引进行排序。
filesort
的字面意思可能会导致混淆,它和文件排序没有任何关系,可以理解为不能利用索引实现排序。
排序一个带
JOIN
(连接)的查询,如果
ORDERBY
子句参考的是
JOIN
顺序里的第一张表的列且不能利用索引进行排序,那么
MySQL
会对这个表进行文件排序(
filesort
),
EXPLAIN
输出中的
Extra
列就有
filesort
。如果排序的列来自于其他的表,且需要临时文件来帮助排序,那么
EXPLAIN
输出的
Extra
列就有“
Using temporary;Using filesort
”字样。对于
MySQL 5.1
,如果有
LIMIT
子句,那么是在
filesort
之后执行
LIMIT
的,这样做效率可能会很差,因为需要排序过多的记录
(一)
两种
filesort
算法
MySQL
有两种
filesort
算法:
two-pass
single-pass
(1) two-pass
这是旧的算法。列长度之和超过
max_length_for_sort_data
字节时就使用这个算法,其原理是:先按照
WHERE
筛选条件读取数据行,并存储每行的排序字段和行指针到排序缓冲(
sort buffer
)。如果排序缓冲大小不够,就在内存中运行一个快速排序(
quick sort
)操作,把排序结果存储到一个临时文件里,用一个指针指向这个已经排序好了的块。然后继续读取数据,直到所有行都读取完毕为止。这是第一次读取记录
.
然后合并如上的临时文件,进行排序。
然后依据排序结果再去读取所需要的数据,读入行缓冲(
rowbuffer
,由
read_rnd_buffer_size
参数设定其大小)。这是第二次读取记录。
以上第一次读取记录时,可以按照索引排序或表扫描,可以做到顺序读取。但第二次读取记录时,虽然排序字段是有序的,行缓冲里存储的行指针是有序的,但所指向的物理记录需要随机读,所以这个算法可能会带来很多随机读,从而导致效率不佳。
优点:
排序的数据量较小,一般在内存中即可完成。
缺点:
需要读取记录两次,第二次读取时,可能会产生许多随机
I/O
,成本可能会比较高。
(2) single-pass
MySQL
一般使用这种算法。其原理是:按筛选条件,把
SQL
中涉及的字段全部读入排序缓冲中,然后依据排序字段进行排序,如果排序缓冲不够,则会将临时排序结果写入到一个临时文件中,最后合并临时排序文件,直接返回已经排序好的结果集
优点:
不需要读取记录两次,相对于
two-pass
,可以减少
I/O
开销。
缺点:
由于要读入所有字段,排序缓冲可能不够,需要额外的临时文件协助进行排序,导致增加额外的
I/O
成本。
(二)
相关参数的设置和优化
相关参数如下。
max_length_for_sort_data
:如果各列长度之和(包括选择列、排序列)超过了
max_length_for_sort_data
字节,那么就使用
two-pass
算法。如果排序
BLOB
TEXT
字段,使用的也是
two-pass
算法,那么这个值设置得太高会导致系统
I/O
上升,
CPU
下降,建议不要将
max_length_for_sort_data
设置得太高。
max_sort_length
:如果排序
BLOB
TEXT
字段,则仅排序前
max_sort_length
个字节。
可以考虑的优化方向如下:
(1)
加大
sort_buffer_size
一般情况下使用默认的
single-pass
算法即可。可以考虑加大
sort_buffer_size
以减少
I/O
需要留意的是字段长度之和不要超过
max_length_for_sort_data
,只查询所需要的列,注意列的类型、长度。
MySQL
目前读取和计算列的长度是按照定义的最大的度进行的,所以在设计表结构的时候,不要将
VARCHAR
类型的字段设置得过大,虽然对于
VARCHAR
类型来说
,
在物理磁盘中的实际存储可以做到紧凑,但在排序的时候,是会分配最大定义的长度的,有时排序阶段所产生的临时文件甚至比原始表还要大。
MySQL 5.7
版本在这方面做了一些优化,有兴趣的同学可以
去了解一下。
(2)
对于
two-pass
算法,可以考虑增大
read_rnd_buffer_size
,但由于这个全局变量是对所有连接都生效的,因此建议只在会话级别进行设置,以加速一些特殊的大操作。
(3)在操作系统层面,优化临时文件的读写。

更多技术资讯可关注:gzitcast