我们日常工作中写 SQL 语句,经常会使用 order by 对记录进行排序。如果 order by 能够使用索引中记录已经排好序的特性,就不需要再借助内存或磁盘空间进行排序,这无疑是效率最高的。然而,还是有各种情况导致 order by 不能够使用索引,而是要进行额外的排序操作。MySQL 把需要借助内存或磁盘空间进行的排序操作统称为文件排序
文件排序
内存排序
在文件夹空白处右键单击鼠标,选择“排序方式”-选择“备注”。文件就会按照修改的备注数值的顺序排列。问题三:文件夹里如何按时间先后顺序排列图标 不同1,打开文件夹,点击右键,选择“排列方式”里的“日期” 2,点击。
本文讲述的文件排序逻辑基于 MySQL 5.7.35 源码。
内容目录
整体概览
排序缓冲区(sort buffer)
单个排序字段太长怎么办?
排序模式4.1 <sort_key,additional_fields>4.2 <sort_key,packed_additional_fields>4.3 <sort_key,rowid>
提升排序效率5.1 优先队列5.2 随机 IO 变为顺序 IO
两类排序6.1 内部排序6.2 外部排序
倒序排序
窥探更多排序细节
总结
1. 整体概览
为了更好的把握 MySQL 文件排序的全局,我们先抛开细节,来看看它的实现过程示意图:
结合上面的示意图,我们来描述一下大体的实现过程:
server 层从存储引擎读取符合 where 条件的记录,写入一个专门存放待排序记录的内存区域,这个内存区域叫做排序缓冲区(sort buffer)
排序缓冲区写满之后,会对缓冲区中的记录进行排序,排好序的记录组成一个数据块
Merge_chunk
数据记录
数据记录
Merge_chunk 和数据记录写入磁盘文件之后,排序缓冲区中的数据被清空,然后就可以写入其它待排序记录了,再次写满之后,缓冲区中的记录同样会进行排序,组成一个数据块,并把 Merge_chunk 和数据记录分别写入磁盘文件 chunk_file 和 temp_file。
读完符合 where 条件的所有记录之后,可能会生成多个数据块,对数据块进行 7 路归并排序,把 7 个小数据块合并成大数据块,直到合并得到的大数据块数量小于等于 15 个(如果归并排序之前,数据块数量本身就小于等于 15 个,此步骤跳过
最后,通过多路归并排序把小于等于 15 个数据块合并为最终的排序结果,写入磁盘文件(out_file)。
2. 排序缓冲区(sort buffer)
排序缓冲区是内存
sort_buffer_size
sort_buffer_size 默认值为 256K,最小可以设置为 32K,最大可以设置为 4G。
3. 单个排序字段太长了怎么办?
order by 子句中,可能会包含一个或多个排序字段,排序字段可以是 int、char、varchar、blob 等各种类型,假设有个字段是这么定义的:a varchar(21845)
排序缓冲区的默认大小为 256K,如果以这样一个字段作为排序字段,就算每条记录只把这一个字段写入到排序缓冲区,写入 4 条记录缓冲区就满了,在记录数量很多情况下,就意味着大量的数据块
max_sort_length
如果单个排序字段内容长度大于 max_sort_length,只有前 max_sort_length 字节的内容会参与排序,以 max_sort_length = 1024 字节
4. 排序模式(sort mode)
排序模式是官方的叫法,实际上就是排序缓冲区或磁盘文件中会写入哪些内容
<sort_key,additional_fields>
<sort_key,packed_additional_fields>
<sort_key,rowid>
这 3 种排序模式的共同点,都会把排序字段(sort_key)写入排序缓冲区或磁盘文件,排序字段可能是一个,也可能是多个。
4.1 <sort_key,additional_fields>
<sort_key,additional_fields> 表示排序缓冲区或磁盘文件中,除了要写入排序字段(sort_key),还要写入存储引擎返回给 server 层的所有字段(additional_fields):
additional_fields 没有描述为 select 子句中的所有字段
存储引擎返回给 server 层的所有字段
上图是写入到排序缓冲区中记录的示意图,以下对各个部分进行说明:
排序字段(sort_key):排序字段内容,会进行编码以节省存储空间,可能包含一个或多个排序字段。
字段 标记区域:创建表时,没有指定 NOT 的字段,在这个区域会有 1 bit 用于标记记录中该字段内容是否为 。
char 长度:char 字段长度,占用 1 字节或 2 字节。<sort_key,packed_additional_fields> 排序模式中,为了节省空间,只写入 char 字段实际内容到排序缓冲区,所以需要记录字段内容长度。为了逻辑统一,<sort_key,additional_fields> 排序模式中也会写入 char 字段长度和内容到排序缓冲区。
char 内容:char 字段内容,以最大长度存储,不会去掉内容尾部的空格
varchar 长度:varchar 字段内容长度,占用 1 字节或 2 字节。
varchar 内容:varchar 字段内容,占用空间为字段最大长度。如果字段实际内容长度小于定义的最大长度,剩余空间留空
除 blob 类型之外的其它字段:指是的除 tinyblob、mediumblob、blob、longblob、tinytext、mediumtext、text、longtext、json、geometry 之外的其它类型字段,只需要把字段内容写入排序缓冲区,不需要写入字段长度。
tinyblob、mediumblob、blob、longblob、tinytext、mediumtext、text、longtext、json、geometry 都是基于 blob 类型实现的,而 select 子句中包含 blob 类型字段时,不能使用 <sort_key,additional_fields>、<sort_key,packed_additional_fields> 排序模式。
重点说明: 如果某个字段内容为 ,该字段在字段 标记区域
<sort_key,additional_fields> 排序模式的好处,只需要从存储引擎读取一次数据,排序缓冲区或排序结果的最终文件(out_file)存放的就是已经排好序的所有记录,读取其中需要的字段返回给客户端就可以了,这是以空间换时间的方式。
如果排序缓冲区不够存储符合 where 条件的所有待排序记录,就需要把缓冲区中的记录排好序之后写入磁盘文件,虽然磁盘相比内存来说,空间可以大很多,但是磁盘 IO 相比内存访问效率低下。<sort_key,additional_fields> 排序模式虽然实现起来简单方便,但也会导致排序缓冲区只能存放更少的待排序记录、需要更多的磁盘 IO、占用更多的磁盘空间,所以,其使用会有所限制。
使用 <sort_key,additional_fields> 需要满足以下条件:
存储引擎返回给 server 层的字段中不能包含 blob 类型字段,因为 blob 字段一般都是用于存储比较长的内容。
排序字段(sort_key)长度之和 +
小于等于
max_length_for_sort_data
如果不满足上面两个条件,会使用 <sort_key,rowid> 排序模式,后面会讲述这种模式。
max_length_for_sort_data 默认值为 1024 字节,最小可设置为 4 字节,最大可设置为 8M。
<sort_key,additional_fields> 的优点是简单方便,它也有缺点,additional_fields 所有字段在排序缓冲区或磁盘文件中都是按照字段最大占用字节数来分配空间的,在以下两种场景中会存在空间浪费:
字段内容为 ,以 utf8 字符集的 varchar 字段为例,假设有字段 a varchar(21845)
char、varchar 类型字段,字段内容实际占用空间小于最大长度,还是以上面的字段 a 为例,如果字段内容为文件排序是怎么实现的
10(字符数)* 3 = 30 字节
为了解决这种排序模式浪费空间的问题,引入了另一种排序模式 <sort_key,packed_additional_fields>
4.2 <sort_key,packed_additional_fields>
<sort_key,packed_additional_fields> 表示排序缓冲区或磁盘文件中,除了要存入排序字段(sort_key),还要存入存储引擎返回给 server 层的所有字段(packed_additional_fields),并且会尽可能使用最少的空间存放待排序记录。
步骤如下:文件夹里面的文件调换顺序的方法,可以右键、查看,然后去掉自动排列图标的勾,就可以随意调换顺序,且不会在刷新后重新排列文档了。1、打开电脑,找到需要处理的文件夹,双击打开该文件夹。2、在文件夹空白处点击鼠标。
字段内容为 时,除 1 bit 的 标记位之外,字段在排序缓冲区不占用额外存储空间;char、varchar 类型字段内容长度小于字段最大长度时,字段在排序缓冲区中只占用实际内容长度大小的空间,不会像 <sort_key,additional_fields> 排序模式一样每个字段都占用字段最大长度大小的空间。
上图是写入到排序缓冲区中记录的示意图,以下对各个部分进行说明:
排序字段(sort_key):排序字段内容,会进行编码以节省存储空间,可能包含一个或多个排序字段。
记录长度:存储排序缓冲区的记录中,除排序字段(sort_key)
记录长度 ~ 除 blob 类型之外的其它字段
字段 标记区域:创建表时,没有指定 NOT 的字段,在这个区域会有 1 bit 用于存储记录中该字段内容是否为 。
char 长度:char 字段长度,占用 1 字节或 2 字节。为了节省空间,只写入 char 字段实际内容到排序缓冲区,所以需要记录字段内容长度。
char 内容:char 字段实际内容,以实际长度存储,会去掉内容尾部的空格
varchar 长度:varchar 字段内容长度,占用 1 字节或 2 字节。
varchar 内容:varchar 字段实际内容。
除 blob 类型之外的其它字段:指是的除 tinyblob、mediumblob、blob、longblob、tinytext、mediumtext、text、longtext、json、geometry 之外的其它类型,只需要把字段内容写入排序缓冲区,不需要写入字段长度。
重点说明: 如果某个字段内容为 ,该字段在字段 标记区域
<sort_key,packed_additional_fields> 是以 <sort_key,additional_fields> 为基础的,如果不满足使用 <sort_key,additional_fields> 的条件,也不会使用 <sort_key,packed_additional_fields>。
使用 <sort_key,packed_additional_fields> 排序模式,还需要满足以下条件:
packed_additional_fields 所有字段最大长度之和必须小于等于 65535 字节
写入排序缓冲区或磁盘文件的一条记录长度必须小于等于
max_length_for_sort_data
可压缩字段最大长度之和必须大于 12 字节,如果可节省的空间太小,也就没必要折腾了。
前面我们讲述了 <sort_key,additional_fields>、<sort_key,packed_additional_fields> 的优缺点及使用限制,如果这两种排序模式都不能使用怎么办?
别急,该轮到 <sort_key,rowid> 排序模式出场了,MySQL 刚出道时采用的就是这种排序模式。
4.3 <sort_key,rowid>
<sort_key,rowid> 表示排序缓冲区或磁盘文件中,除了要存入排序字段(sort_key),还要存入记录的主键 ID(rowid)。
上图是写入到排序缓冲区中记录的示意图,相比其它两种排序模式来说,非常简单,不做过多说明了。
想必大家应该发现了 <sort_key,rowid> 和 <sort_key,additional_fields>、<sort_key,packed_additional_fields> 的区别了,使用 <sort_key,rowid> 时,排序缓冲区或磁盘文件中只包含了记录的主键 ID,而客户端可能需要除主键之外的字段,怎么办?
这就要二次访问存储引擎了,第一次从存储引擎拿到符合 where 条件的所有记录的主键 ID
使用 <sort_key,rowid> 读取数据的过程,有点类似根据 ID 批量从 redis 获取详细数据的过程,先从某个 redis key 读取多个 ID,然后根据 ID 读取缓存的详细数据。
这种排序模式的好处是记录数量不太多的情况下,使用排序缓冲区就能够存储所有待排序记录了,能够在一定程度上避免使用磁盘文件排序。
举例说明:假设排序字段为 int 类型,主键也为 int 类型,写入排序缓冲区的一条记录占用的空间为 4 + 4 = 8 字节,排序缓冲区的默认大小为 256K,能够存放 32768 条记录,对于一般业务来说,都不会一次性读取这么多记录,由此可见,一般情况下,使用 <sort_key,rowid> 排序模式不会用到磁盘文件排序。
但是,凡事都有例外,如果一条 SQL 语句读取过多的记录,哪怕是使用 <sort_key,rowid>,当排序缓冲区满时,也需要把缓冲区中的记录排好序组成一个数据块
细心的小伙伴可能发现了一点情况,到现在为止讲的三种排序模式,都是把符合 where 条件的所有记录
MySQL 为了提升性能,想了各种办法,使用了各种技巧,对于上面提到的这种情况,自然也还可以再优化,我们一起来看看吧。
5. 提升排序效率
5.1 优先队列
通过前面的讲述,我们已经知道了,MySQL 会把符合 where 条件的所有记录写入排序缓冲区,当缓冲区满时,缓冲区中的记录排好序后组成一个数据块
避免使用磁盘文件
优先队列在内存中维护待排序记录的队列,队列中的元素不会实际存储记录内容,而是存储指向排序缓冲区中记录的地址,因为要保证不使用磁盘文件,使用优先队列提升排序效率的必要条件
必须包含 limit
在满足必要条件的基础上,会评估使用优先队列进行排序是否更快,以决定是否使用。
使用优先队列提升排序效率,借鉴的是大顶堆
最大或最小的 limit 条记录
不使用磁盘文件排序,避免磁盘 IO。
只需要对一部分待排序记录中进行排序。
如果排序缓冲区不能
limit + 1 中的是优先队列需要使用的额外的一条记录的空间。
如果排序缓冲区能够存放
优先队列 + 排序缓冲区
只使用排序缓冲区
3 倍
优先队列 + 排序缓冲区
只使用排序缓冲区
经过前面一系列的成本评估之后,如果还是不能使用优先队列,MySQL 会进行最后一次尝试,这就是最后一哆嗦了:如果使用的排序模式是 <sort_key,additional_fields>,可能会造成需要使用磁盘文件排序,此时会判断使用 <sort_key,rowid> + 优先队列
<sort_key,additional_fields> + 磁盘文件排序
如果使用<sort_key,additional_fields> + 磁盘文件排序
如果使用<sort_key,rowid> + 优先队列
看到这里,可能有的小伙伴会有疑问,排序模式 <sort_key,additional_fields> 是不是和优先队列两者不能共存?
<sort_key,文件夹里的文件怎么自由排序,additional_fields> 和优先队列是可以共存的,只有当使用 <sort_key,additional_fields> 排序模式导致排序缓冲区不能存放所有待排序记录,要借助磁盘文件实现排序时,如果改为使用<sort_key,rowid> + 优先队列
5.2 随机 IO 变为顺序 IO
使用 <sort_key,rowid> 排序模式,从存储引擎读取符合 where 条件的所有记录的主键 ID,按照 sort_key 排序好序之后,需要根据主键 ID 从存储引擎读取记录中的需要返回给客户端的其它字段。按照 sort_key 排好序的记录,在主键索引中不一定是顺序存储的,可能是分散在主键索引的不同叶子节点中,这样一来,通过主键 ID 一条一条去存储引擎读取记录,会造成大量随机 IO,导致从存储引擎读取数据效率低下。
为了尽可能减少随机 IO,MySQL 通过一个专门的内存区域,尽量把随机 IO 变成顺序 IO,这个专门的内存区域为 随机读缓冲区(read_rnd_buffer)
缓冲区大小由系统变量 read_rnd_buffer_size
随机 IO 变为顺序 IO 的实现逻辑是这样的:
从最终的排序结果磁盘文件(out_file)读取主键 ID,写入随机读缓冲区
写满之后,对随机读缓冲区
排好序之后,再按照主键 ID 逐条从存储引擎读取记录中需要返回给客户端的其它字段。
只有当排序缓冲区存放不下所有记录的 <sort_key,rowid>,需要使用磁盘文件来存储时,才有可能使用随机缓冲区来优化文件排序,然而,这还只是入门门槛,要想使用随机缓冲区,需要满足的其它条件达 9 条之多,涉及到源码细节,就不一一展开了,大家只要知道有这么一种优化方案存在就可以了。
使用 <sort_key,additional_fields>、<sort_key,packed_additional_fields> 排序模式时,不需要使用随机缓冲区来优化文件排序,因为这两种排序模式下,需要返回给客户端的所有字段都已经在排序缓冲区或磁盘文件(out_file)中了,不需要通过主键 ID 二次访问存储引擎读取其它字段。
6. 两类排序
MySQL order by 的实现过程,可能会进行两类排序:内部排序、外部排序。
前面多次提到,当排序缓冲区满,会把缓冲区中的记录排好序,组成一个数据块
内部排序
符合 where 条件的所有记录都写入到磁盘文件之后,可能会存在多个已经内部排好序的数据块
外部排序
6.1 内部排序
内部排序是对排序缓冲区中的记录进行排序,是内存排序。
为了提升性能,MySQL 做了各种各样的努力,内部排序的实现又一次体现了这种追求极致性能的努力。内部排序使用了 3 种排序算法:
基数排序如果排序字段内容长度之和小于等于 20 字节
大于等于 1 千,小于 10 万
快速排序如果不满足使用基数排序的条件,则会考虑使用快速排序
小于等于 100
归并排序归并排序是兜底的排序算法,如果不满足使用基数排序和快速排序的条件,就会使用归并排序
为什么使用快速排序的条件是排序记录数量小于等于 100?源码注释是这样说的,归并排序比快速排序更快,但是归并排序申请临时缓冲区需要额外的时间成本,所以在排序记录数量很少的时候,归并排序并没有多大优势,归并排序比快速排序快的临界点是排序记录数量在 10 ~ 40 条之间
6.2 外部排序
外部排序是对磁盘文件中已经局部排好序的记录进行全局归并排序,是磁盘文件排序。
MySQL 从存储引擎读取符合 where 的条件记录写入排序缓冲区,缓冲区满时,会对缓冲区中的记录进行内部排序
Merge_chunk
数据记录
Merge_chunk 写入到磁盘文件(chunk_file)中,数据记录写入到磁盘文件(temp_file)中。Merge_chunk 中保存有数据记录在 temp_file 中的起始位置
记录数量
从存储引擎读取完符合 where 条件的所有记录之后,可能会生成多个数据块写入到磁盘文件(temp_file)。通过多路归并排序,把小数据块合并为更大的数据块,最终得到全局排好序的记录,把磁盘文件(temp_file)中多个数据块合并为一个数据块的过程,借助了优先队列
排序缓冲区
注意:外部排序过程中借助优先队列和排序缓冲区,和 5.1 优先队列
优先队列 + 排序缓冲区
外部排序把小数据块合并为大数据块的过程中,会使用 7 路归并排序,把 7 个小数据块合并为 1 个大数据块,数据块数量多时,会进行多轮归并排序,直到数据块的数量小于等于 15
接下来我们以磁盘文件(temp_file)中包含 160 个数据块为例来说明外部排序的过程。
第一轮归并排序示意图::
左下角 chunk_file 中有 160 个 Merge_chunk,temp_file 有 160 个对应于 Merge_chunk 的数据记录块
子排序缓冲区
数据记录块
子排序缓冲区
1、点击选中文件夹,按F2重命名,在文件名前面添加序号。2、把常用的文件夹添加“1、2、3”这样可以使其排列在最前方。3、如果文件夹没有根据序号进行排列,鼠标右键空白处点击“排列方式”——“名称”。4、这样文件夹就。
读取 temp_file 中数据记录块
current_key
current_key 指向的记录是子排序缓冲区对应的 temp_file 数据记录块中排序字段值(sort_key)最大的那条记录。
归并排序过程中,会循环把 7 个 Merge_chunk 的 current_key 指向的记录中排序字段值最大的记录写入到 temp_file2
数据记录块
每次都取 current_key 指向的记录中最大的记录,有没有发现这是大顶堆
从图中右下角的 temp_file2
chunk_file
第二轮归并排序示意图:
第一轮归并排序,我们已经讲述了详细的合并过程,第二轮归并排序就不展开讲了,由上图右下角的 temp_file
chunk_file
企业回1.当然可以,首先要确定自己有一个pdf文档,而且确定目的是需要将pdf文档转换为word格式,然后我们将现有文档通过迅捷pdf转换器软件打开。2.通过选择PDF转换页面功能其中的PDF文件转WORD项目拖入文件。3.接着在迅捷pdf转换器下方按自己的需要是。
中间结果
最终排序结果
不知道大家有没有发现,第一轮归并排序示意图中,是从 temp_file 读取记录到子排序缓冲区,然后把归并排序结果写入到 temp_file2;第二轮归并排序示意图中,是从 temp_file2 读取记录到子排序缓冲区,然后把归并排序结果写入到 temp_file。这说明在外部归并排序过程中,会使用两个中间结果磁盘文件(temp_file、temp_file2),加上 chunk_file、out_file,一共会使用 4 个磁盘文件,大家知道一下就可以了。
终极多路归并排序:
从上图可见,经过前面两轮归并排序之后,得到 4 个数据块,排序缓冲区被分为 4 个子缓冲区,4 个子缓冲区中已局部排好序的记录,经过归并排序写入到存放最终排序结果的磁盘文件中(out_file)。
最后一轮归并排序和前面的 N 归并排序有些不同。
前面 N 轮归并排序,写入到磁盘文件的是中间结果
最后一轮归并排序,写入到磁盘文件的是最终结果
7. 倒序排序
MySQL 文件排序的内部实现中,正序和倒序排序都是以正序的方式进行的,排序字段值大的在前面,排序字段值小的在后面,这样逻辑统一,实现方便。
倒序排序时,把排序字段值写入排序缓冲区之前,会对所有排序字段值进行逐字节取反
1、电脑打开文件夹。2、电脑打开文件夹之后,可以点击右上角的查看进行随意排序。3、也可以右键点击空白处,然后选择排序方式,然后选择任意一种排序方式。4、随意选择一种排序方式之后,文件夹里面的文件就会进行排序了。
举例说明
select num from t order by num desc
以 <sort_key,additional_fields> 排序模式为例,假设表中有 5 条记录,num 字段值分别为:95,90,49,97,6,num 字段值会写入到排序缓冲区两次,一次是作为 sort_key,一次是作为 additional_fields,5 条记录全部写入缓冲区之后,缓冲区的内容示意如下:
图中蓝色块
绿色块
从 3 号图示可见,倒序排序时,对排序字段值取反之后按照正序排序,最终实现了记录的倒序排序
上面示例中,对于 num 字段会写入排序缓冲区两次,可能有的小伙伴会有疑问,这里解释一下:实际上,排序字段作为 sort_key 写入到排序缓冲区之前,都会进行编码,编码之后的排序字段值就和原字段值不一样了,只用于排序,不作为最终结果返回给客户端,在排好序之后,sort_key 会被丢弃。
8. 窥探更多排序细节
通过 explain,我们只能看到 SQL 语句执行时,是否使用了文件排序,看不到排序过程中是只使用了排序缓冲区,还是也使用了磁盘文件,更不知道排序缓冲区被写满了多少次;也看不到是不是使用了优先队列,如果没有使用,是什么原因。
好在 MySQL 给我们提供了一个工具(optimizer trace
-- 开启 optimizer traceset optimizer_trace = "enabled=on";-- 设置 optimizer_trace 输出结果最多可占用的内存空间,单位是:字节-- 如果发现 optimizer_trace 的 json 结果不全,可以把这个值改成更大的数字set optimizer_trace_max_mem_size = 1048576;
optimizer trace 的输出结果 json 中有两个和文件排序相关的属性:filesort_summary
filesort_priority_queue_optimization
/ filesort_summary{"rows": 10227,"examined_rows": 10227,"number_of_tmp_files": ,"sort_buffer_size": 262112,"sort_mode": "<sort_key,rowid>"}
number_of_tmp_files:
等于 0 表示只使用了排序缓冲区。
大于 0 表示还使用了磁盘文件,number_of_tmp_files 的值等于几,就表示排序缓冲区被写满了几次,也意味着写入到磁盘文件的数据块有几个。
大家不要被 number_of_tmp_files
相信经过本文前面的讲述,大家对于 sort_mode 都已经比较熟悉了,一共有三种排序模式:<sort_key,rowid>、<sort_key,additional_fields>、<sort_key,packed_additional_fields>。
/ filesort_priority_queue_optimization{"usable": false,"cause": "not applicable (no LIMIT)"}
通过 usable = false
有一点需要特别注意,获取 optimizer trace 结果的语句必须和 SQL 语句同时执行,不能分开一条一条的执行,否则查出来的 optimizer trace 结果是空白。
举例说明:
select * from t2 where i1 >= 99991840 order by str1; -- SQL 1select * from information_schema.OPTIMIZER_TRACE; -- SQL 2
对于上面例子,如果先执行 SQL 1
SQL 2
9. 总结
max_sort_length
排序模式
<sort_key,additional_fields> 把排序字段(sort_key)和存储引擎返回给 server 层的字段都写入排序缓冲区或磁盘文件,每个字段都以其最大长度占用存储空间,存在在空间浪费。
<sort_key,packed_additional_fields> 是 <sort_key,additional_fields> 的改进版,解决了空间浪费的问题。char、varchar 字段只占用实际内容需要的空间,内容为 的字段除了在 标记区域占用 1 bit 之外,不会再占用其它空间。
前面两种排序模式,以空间换时间,虽然不需要两次访问存储引擎,让文件排序逻辑整体上更简单,但是记录数量多起来之后,需要磁盘文件存储排序结果,而磁盘 IO 会导致排序效率低下。<sort_key,rowid> 需要两次
提升排序效率
SQL 语句中包含 limit 的情况下,通过成本评估有可能会使用优先队列来避免磁盘文件排序,提升排序效率。
使用 <sort_key,rowid> 排序模式,并且由于记录数量太多需要使用磁盘文件时,通过把主键 ID 缓存在随机读缓冲区(read rnd bufer),缓冲区满后对主键 ID 排序,再通过主键 ID 去存储引擎读取客户端需要的字段,尽可能把随机 IO 变为顺序 IO,提升排序效率。
两类排序
外部排序,可能会进行 0 ~ N 轮归并排序生成中间结果
最终结果
倒序排序
最后,介绍了如何通过 optimizer trace 窥探文件排序的更多细节。
我们一起学习更多 MySQL 知识