Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Mysql如何使用order by工作

Mysql如何使用order by工作

作者头像
小土豆Yuki
发布于 2020-11-03 03:34:03
发布于 2020-11-03 03:34:03
1.1K00
代码可运行
举报
文章被收录于专栏:洁癖是一只狗洁癖是一只狗
运行总次数:0
代码可运行

日常开发中,我们经常要进行字段的排序,但是我们大多不知道排序是如何执行的,今天我们就说说order by 的执行逻辑,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`city` varchar(16) NOT NULL,
`name` varchar(16) NOT NULL,
`age` int(11) NOT NULL,
`addr` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`),
KEY `city` (`city`)
) ENGINE=InnoDB

如果我们执行下面语句是如何进行排序的呢

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select city,name,age from t where city='杭州' order by name limit 1000  ;

全字段排序

之前我们说过,为了避免全表扫描,我们在city字段上加索引,现在我使用explain命令查看这个语句的执行情况

我们发现extra这个子弹中的Using filesort 表是要进行排序,Mysql为每一个线程分配一块内存用于排序,这个叫sort_buffer.

如图所示,通常情况下,这个语句的流程如下

  1. 初始化sort_buffer,确定放入name,city,age这三个字段
  2. 从索引中找到第一个杭州的主键id
  3. 然后到主键id取出整行(name,age,city),存入sort_buffer中,
  4. 从索引字段中去下一个记录的id
  5. 重复3,4步骤,直到不满足条件
  6. 对sort_buffer中的name字段进行排序
  7. 按照排序结果取前1000条返回给客户端

我们把上面的排序叫全字段排序,执行流程如下

图中nama的排序有可能在内存中完成,也就可能使用外部排序,这个取决于所需的内存和参数sort_buffer_size

sort_buffer_size,就是Mysql为排序开辟的内存的大小,如果排序的数据量小于sort_buffer_size,排序就在内存中排序,如果大于内存大小,就会使用磁盘的临时文件辅助排序,

我们可以使用下面方法,来确定一个排序语句是否使用了临时文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* 打开optimizer_trace,只对本线程有效 */
SET optimizer_trace='enabled=on';

/* @a保存Innodb_rows_read的初始值 */
select VARIABLE_VALUE into @a from  performance_schema.session_status where variable_name = 'Innodb_rows_read';

/* 执行语句 */
select city, name,age from t where city='杭州' order by name limit 1000;

/* 查看 OPTIMIZER_TRACE 输出 */
SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G

/* @b保存Innodb_rows_read的当前值 */
select VARIABLE_VALUE into @b from performance_schema.session_status where variable_name = 'Innodb_rows_read';

/* 计算Innodb_rows_read差值 */
select @b-@a;

这个方法是通过查询OPTIMIZER_TRACE结果来确认,你可以从number_of_tmp_files看到是否使用了临时文件,

number_of_tmp_files表示使用的临时文件数,我们可以理解为mysql在排序的时候把数据分成了12份,每一份单独排序后存在这些临时文件中,然后把12有序文件再合并一个有序的大文件。

如果number_of_tmp_files=0,说明使用的是内存进行排序,我们看到examined_rows=4000.说明参与排序的行数是4000.sort_mode里面packed_additional_fields的意思是说,排序过程对字段进行了紧凑处理,如果字段定义为varchar(16),实际上排序过程中也就是按照字段的实际长度进行排序。

注意的是这里我们设置internal_tmp_disk_storage_engin设置成MyiSAM,否则select @a-@b的结果会显示4001,这是因为如果是innodb,数据从临时表取出也会进行加1操作。

rowid排序

我们可以看到如果查询的字段很多的话,那么sort_buffer存放的字段数太多,就会使用临时文件进行排序,因此造成了很大的浪费,此时mysql任务排序的单行长度会怎么做呢,

首先我要知道如何判断单行长度太大,如下参数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SET max_length_for_sort_data = 16;

我们看到city,name,age总长度为36,远远大于16,因此我们判定单行长度过大,Mysql就会使用另外一种算法进行排序.

新的算法放入sort_buffer的字段,只需要排序的列和主键id,但是这个时候,并不能直接返回排序的结果,而是要回表查询整行。

  1. 初始化sort_buffer,确定放入两个字段,即name和id
  2. 从索引city中找到第一个满足的条件主键id
  3. 再到主键id索引中获取整行,取出name,id两个字段,存入sort_buffer
  4. 在从索引city中到下一个记录id
  5. 重复3,4步骤,知道不满足条件位置
  6. 对sort_buffer进行name排序
  7. 遍历排序结果,取出前1000条记录, 并按照id再到原表获取city,name,age字段返回给客户端

上面的排序算法叫做rowid排序,对比之前的流程,我们发现不同于之前的是多了一次访问表T的步骤,我们可根据上图的步骤,想一下select @b-@a,结果是啥,

首先,图中examined_rows的值还是4000,表示用于排序的数据是4000行,但是select@b-@a这个语句的值变成5000.

因为这个时候除了排序过程外,在排序完成后,还要根据id取原表取值,由于语句是limit 1000,因此会多读1000行。

我们也可以发现sort_mode=sort_key,rowid,表示参与排序只要name和id两个字段.

numner_of_tmp_files=10,那是因为参与排序的行数虽然仍然是4000行,但是每一个行都变小了,因此需要排序的总数量变小了,需要的临时文件相应变少了。

全字段排序和rowid排序

如果msyql实在是担心内存太小,会影响排序效率,才会采取rowid算法,这样排序过程中一次可以排序更多行,但是需要回表取数据。

如果任务内存足够大,会优先选择全字段排序,把需要的字段放入到sort_buffer,这样就会直接从内存里面返回查询结果,不再回表查询数据,

对于innodb来说,rowid排序要求回表造成磁盘读,因此不会优先选择,

看到这里,是不是所有的order by都要进行排序操作,如果不排序就不能获取正确的数据呢,其实,并不是多有的order by 语句,都需要排序,MySQL之所以要使用临时文件排序,是因为原来的数据都是无序的,因此如果本身的从city索引获取的数据就是按照name进行排序的,是不是就可以不用再进行排序呢.

实时上,确实是这样的,我们现在来建立一个(city,name)联合索引,对应的sql语句如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
alter table add index city_user(city,name)

从上面索引看出,我们依然可以用树索引定位到第一个city='杭州'的数据,并且额外确保了,接下来按顺序去下一条记录,只要city=杭州,name就是有序的

  1. 从索引(city,name)找到一个满足city=杭州条件的主键id
  2. 到主键id取到整行,取name,age ,city,作为结果的一部分直接返回
  3. 从索引(city,name)取下一个主键id
  4. 重复2,3步骤,直达查询到1000记录,或者不满足条件循环结束

可以看到这个查询过程不需要临时表,也不需要排序,接下来,我们用explain结果验证一下

发现extra的字段中没有using filesort,也就是不用排序,而且由于(city,name)索引本身就是索引有序,所以这个查询不需要查询4000行数据,只要找到前1000条数据就可以了。

到这里,我是不是还可以进行优化呢,当然是可以的,我们可以使用覆盖索引,覆盖索引是指,索引上的信息足够满足查询请求,不需要再回到主键索引上取数据,

我们按照覆盖索引的概念,建立(city,name.age)联合索引,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
alter table add index (city,name,age)

这里如果city相等,还是按照name字段进行递增进行排序的,此时查询语句也就不需要排序了,

  1. 从索引(city,name,age)中找到满足city=杭州的记录,取出city,name,age这三个字段的值,作为结果集的一部分返回
  2. 从索引(city,name,age)取下一个记录,同样取出三个字段的值,作为结果返回
  3. 重复2步骤,直到查到1000记录,或者不满足city=杭州的条件结束循环

然后我们再看explain

可以看到Extra字段里面多了Using index ,表示使用了覆盖索引,性能上会快很多.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
四面阿里被问MySQL底层如何实现order by的,瞬间懵了!
Extra的 Using filesort 表示需要排序,MySQL会给每个线程分配一块内存(sort_buffer)用于排序。
JavaEdge
2021/04/14
1.6K0
四面阿里被问MySQL底层如何实现order by的,瞬间懵了!
面试官:order by 怎么优化?
刚换了新工作,用了两周时间准备,在 3 天之内拿了 5 个 offer,最后选择了广州某互联网行业独角兽 offer,昨天刚入职。这几天刚好整理下在面试中被问到有意思的问题,也借此机会跟大家分享下。
JavaFish
2021/07/05
2.4K0
面试官:order by 怎么优化?
mysql 之order by工作流程
Extra 中 Using index condition; 这个是之前文章中提到的索引下推 ICP Using filesort 这个表示需要排序 mysql会给每个线程分配一块内存 叫做sort_buffer
科技新语
2025/03/20
680
mysql 之order by工作流程
MySQL Order By工作原理
Extra中包含Using filesort表示需要排序,在排序时,MySQL会为每个线程分配一块内存区域用于排序,称之为sort_buffer。
shysh95
2022/02/16
8380
MySQL Order By工作原理
看一遍就理解:order by详解!
日常开发中,我们经常会使用到order by,亲爱的小伙伴,你是否知道order by 的工作原理呢?order by的优化思路是怎样的呢?使用order by有哪些注意的问题呢?本文将跟大家一起来学习,攻克order by~
macrozheng
2021/07/02
1.3K0
看一遍就理解:order by详解!
MySQL深入学习第十七篇-如何正确地显示随机消息?
我在上一篇文章,为你讲解完 order by 语句的几种执行模式后,就想到了之前一个做英语学习 App 的朋友碰到过的一个性能问题。今天这篇文章,我就从这个性能问题说起,和你说说 MySQL 中的另外一种排序需求,希望能够加深你对 MySQL 排序逻辑的理解。
越陌度阡
2020/11/26
5910
MySQL深入学习第十七篇-如何正确地显示随机消息?
MySQL 学习笔记【索引篇】
索引是一种数据结构。官方描述为:索引(Index)是帮助MySQL高效获取数据的数据结构。因此我们针对索引的使用和优化,本质上也是基于一种特殊的数据结构进行的优化。总结下innodb的索引特点:
Porco1Rosso
2020/01/02
9650
MySQL 学习笔记【索引篇】
Mysql如何随机获取表中的数呢rand()
随机获取数据的业务场景,想必大家都有遇到过,今天我们分析一下如何正确的显示随机消息.
小土豆Yuki
2020/11/03
4.7K0
order by的工作原理
where条件后面是city字段,然后根据name排序,可以看到,执行计划中有:using filesort字样。这是因为name字段没有索引,所以需要借助sort_buffer来进行排序操作。
AsiaYe
2020/06/22
7440
MySQL怎样处理排序⭐️如何优化需要排序的查询?
在MySQL的查询中常常会用到 order by 和 group by 这两个关键字
菜菜的后端私房菜
2024/06/21
2060
技术分享 | 排序(filesort)详细解析(8000 字长文)
文章末尾有他著作的《深入理解 MySQL 主从原理 32 讲》,深入透彻理解 MySQL 主从,GTID 相关技术知识。
爱可生开源社区
2020/04/08
1.2K0
技术分享 | 排序(filesort)详细解析(8000 字长文)
工作中遇到的99%SQL优化,这里都能给你解决方案(二)
利用最左前缀法则:中间字段不能断,因此查询用到了name索引,从key_len=74也能看出,age索引列用在排序的过程中,因为Extra字段里没有using filesort。
程序员小强
2019/09/10
4790
工作中遇到的99%SQL优化,这里都能给你解决方案(二)
MySQL实战45讲 笔记
不要小看一条 update 语句,在生产机上使用不当可能会导致业务停滞,甚至崩溃。
OwenZhang
2021/12/08
7240
MySQL实战45讲 笔记
MySQL排序内部原理探秘
一、我们要解决什么问题 二、排序,排序,排序 三、索引优化排序 四、排序模式 4.1实际trace结果 4.2排序模式概览 4.2.1回表排序模式 4.2.2不回表排序模式 4.2.3打包数据排序模式 4.2.4三种模式比较 五、外部排序 5.1普通外部排序 5.1.1两路外部排序 5.1.2多路外部排序 5.2MySQL外部排序 5.2.1MySQL外部排序算法 5.2.2sort_merge_passes 六、trace 结果解释 6.1 是否存在磁盘外部排序 6.2 是否存在优先队列优
沃趣科技
2018/03/26
2.7K0
MySQL排序内部原理探秘
MySQL ORDER BY 实现原理
假设有一张表 tb_user 表,表里有 5 个字段 id、name、age、city、created_at。
恋喵大鲤鱼
2024/01/26
2040
MySQL - order by 出现 using filesort根因分析及优化
当然了实际工作中是基本不会出现这种情况的, 假设真的取了100万数据, 无论是MySQL内存缓冲区的占用,还是网络带宽的消耗都是巨大的。
小小工匠
2021/11/10
6.6K0
MySQL - order by 出现 using filesort根因分析及优化
如何正确的使用 order by
根据已有的知识,birth_city 字段出现在where条件中,我们在该字段上建立索引能加快访问速度。那么该语句的查询过程如下:
用户7447819
2021/07/23
2.1K0
SQL优化思路+经典案例分析
SQL调优这块呢,大厂面试必问的。最近金九银十嘛,所以整理了SQL的调优思路,并且附几个经典案例分析。
捡田螺的小男孩
2023/02/24
9720
SQL优化思路+经典案例分析
MySQL排序原理与优化方法(9/16)
**内存临时表排序:**在MySQL中,使用InnoDB引擎执行排序操作时,当处理的数据量较小,可以在内存中完成排序时,MySQL会优先使用内存进行排序操作。在这种情况下,MySQL会创建一个临时内存表来存储排序结果,这样可以快速地对数据进行排序,提高查询效率。
十里桃花舞丶
2024/04/12
2210
MySQL随机函数RAND
Extra中Using temporary表示使用临时表,Using filesort表示需要执行排序操作。
shysh95
2022/02/16
2.6K0
MySQL随机函数RAND
相关推荐
四面阿里被问MySQL底层如何实现order by的,瞬间懵了!
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验