其它MySQL 面试系列:
MySQL 面试系列:为什么MySQL字符串不加引号索引失效?
MySQL 面试系列:如何选择普通索引和唯一索引?
MySQL 面试系列:一条 select 语句在 MySQL 是这样执行的?
MySQL 面试系列:MySQL查询如何进行优化?
MySQL 面试系列:MySQL 常见的开放性问题
MySQL 面试系列:MySQL 性能优化 & 分布式
MySQL 面试系列:MySQL 命令和内置函数
MySQL 面试系列:MySQL 中日志的面试题总结
MySQL 面试系列:MySQL 中锁的面试题总结
MySQL 面试系列:MySQL 事务的面试题总结
MySQL 面试系列:MySQL 索引的面试题总结
MySQL 面试系列:MySQL 基础模块的面试题总结
在日常开发工作中,你一定会经常遇到要根据指定字段进行排序的需求。
这时,你的 SQL 语句类似这样。
select id,phone,code from evt_sms where phone like '13020%' order by id desc limit 10
这个 SQL 的逻辑是十分清晰明了,但其内部的执行原理你知多少。
接下来,本期文章将带你打开 order by 的大门一探究竟。
本期所有结论都基于MySQL8.0.26版本
一、常见的 Extra几个信息
在 MySQL中想看一条 SQL 的性能不仅仅看是否用上了索引,还要看Extra中的内容,以下内容来自官方文档,给你最准确的学习资料。
using index
根据索引树可直接检索列信息,无需额外的操作来读取实际的行。
索引列即为查询列,也为条件列。
using index condition
下面这条语句name为普通索引,age无索引。
select * from table where name = ? and age = ?
索引下推是在MySQL5.6及以后的版本出现的。
之前的查询过程是,先根据name在存储引擎中获取数据,然后在根据age在server层进行过滤。
在有了索引下推之后,查询过程是根据name、age在存储引擎获取数据,返回对应的数据,不再到server层进行过滤。
当你使用Explain分析SQL语句时,如果出现了using index condition那就是使用了索引下推,索引下推是在组合索引的情况出现几率最大的。
using index for group_by
只查索引列,对索引列使用了group by
select phone from evt_sms where phone = "13054125874" group by phone
using where
查询的列被索引覆盖,并且where筛选条件是索引列之一,但不是索引的前导列,Extra 中为Using where; Using index, 意味着无法直接通过索引查找来查询到符合条件的数据
查询的列被索引覆盖,并且where筛选条件是索引列前导列的一个范围,同样意味着无法直接通过索引查找查询到符合条件的数据
zero limit
这个估计很少有小伙伴知道,就是你的SQL语句查询数量为limit 0
using temporary
使用了临时表,一般在使用group by、order by时会遇到。
这个也是本文即将要聊的话题。
using filesort
一般在使用group by、order by时会遇到,排序过程在内存中完成
Backward index scan
对索引列使用了降序操作
这里只列举了最常见的几个信息,MySQL 官方文档中对 Extra 的解析大概有37个,感兴趣的可以去看看,后期也会逐步完善这块内容。
二、文件排序
由于是在一些统计、排序的业务中会经常见到Extra中出现using filesort的信息。
在MySQL8.0.26 版本中对一个没有索引的列进行排序在Extra中显示using filesort。在低版本中需要你进行试验在什么情况下会出现。
image
在Extra中显示的using filesort表示的就是排序,MySQL会给每个线程分配一块内存用于排序,也被称之为sort_buffer。这期文章和下期文章会牵扯到很多名词,记得自己整理一下哈!
再看这条语句
image
那么这条SQL执行的具体流程是什么呢?
1、初始化sort_buffer,放入字段phone、code字段
2、在phone的索引树找到主键值
3、根据主键值到主键索引树中检索处phone、code对应字段的值,再存储sort_buffer中
4、继续从phone取下一个主键值
5、重复第三、第四,直到不满足phone = 条件为止
6、在sort_buffer中的数据按照字段phone做快排
7、按照快排的结果取出前10行返回改客户端即可
问题:所有的排序都是在内存中进行的?
当然不是,任何内存都不是无限制的,是否在内存中排序取决于MySQL 参数 sort_buffer_sort。
image
在 MySQL8.0.26 版本中这个值大小默认为 256kb。
当需要排序的数据量大于 256kb 的阀值时,则会利用临时文件进行辅助排序,也就是常说的归并排序算法实现。
sort_buffer_size 跟需要临时文件的个数成正比,如果 sort_buffer_size 越小则临时文件的数量就越多。
如何查看一个排序是否使用了临时文件,这个答案就交给大家来实现,版本不一致会导致很多结果都不同。
问题:你知道归并排序是如何实现的吗?
现在你知道了如果排序的数据大于sort_buffer_size会使用临时文件排序,这种排序使用的就是归并排序的思想,接下来让我们看看具体的流程是怎么样的。
1、把需要排序的数据分割,分割成每块数据都可以存放到 sort_buufer 中
2、对每块数据在 sort_buufer 中进行排序,排序好后,写入某个临时文件
3、当所有的数据都写入临时文件后,这时对于每个临时文件内部来说是有序的,但对于所有临时文件是无序的,所以还需要合并数据
4、假设现在存在 tmp1 和 tmp2 两个临时文件,这时分别从 tmp1、tmp2 读入部分数据到内存
5、假设从 tmp1 和 tmp2 中分别读入[0-5]的数据,然后分别使用 tmp1[0]、tmp2[0] 进行对比,一直到 tmp1[5]、tmp2[5],这样两两比较就可以把 tmp1、tmp2 合并为一个文件。经过几轮下来所有分割的数据都会合并为一个有序的大文件
三、文件排序很慢,还有其它办法吗
通过上面的案例,如果排序的数据量非常大则会超过 sort_buffer_size 的最大值,就只能使用文件排序,文件排序涉及了多次的文件合并是非常消耗性能的。
在上文你有没有发现一个细节,SQL中只需要排序code字段,但把 phone 字段也加到了 sort_buufer 中了。
这样单行的数据大小无形中就增大了,这样内存中能够存放的行数就减少了,需要分割成多个临时文件,排序性能会很差,那么有没有其它方案可以解决这种问题呢?
答案是肯定有的,就是接下来要聊的 rowid 排序。
先看一个参数 max_length_for_sort_data














网友评论