mysql 之order by工作流程

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 本文深入解析了MySQL中`ORDER BY`的排序机制,通过具体示例展示了排序过程及性能优化方法。文章首先分析了基于内存和磁盘的排序方式,包括`sort_buffer_size`的影响以及临时文件的使用场景。接着介绍了`rowid`排序算法,该算法通过减少参与排序的数据量来提升性能,并对比了其与传统排序的区别。此外,还探讨了随机查询`ORDER BY RAND()`的执行流程及其优化策略。最后提到了MySQL 5.6引入的优先队列排序算法,适用于仅需部分有序结果的场景。文章结合`optimizer_trace`工具详细说明了各配置参数对排序行为的影响,为优化查询提供了实用指导。

用下面这个表为列 来看看MySQL order by 的工作方式

代码解读

复制代码

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;

     现在有个需求 查询城市为杭州的所有人的姓名 并且按照姓名的排序返回前1000位的name age ???

SQL 如下:

代码解读

复制代码

  select city,name,age from t where  city = '杭州' order by name limit 1000;

贴出explain SQL的执行结果

Extra 中 Using index condition; 这个是之前文章中提到的索引下推 ICP Using filesort 这个表示需要排序 mysql会给每个线程分配一块内存  叫做sort_buffer

需要注意的是 order by name 这个动作 有可能在内存中完成 也可能使用外部排序 这取决于排序需要的内存和sort_buffer_size

sort_buffer_size 也就是MySQL为排序开辟的内存空间 如果要排序的数据量小于sort_buffer_size 排序就在内存中完成 如果大的话就需要利用磁盘临时辅助排序

我们可以利用 optimizer_trace   来确定一个排序语句是否使用了临时文件 具体的分析 看optimizer_trace 这篇文章 我直接贴图了

  1. number_of_tmp_files 表示排序过程中使用的临时文件 如果number_of_tmp_files = 0 表示没有用到临时文件 上图为4 表示使用了外部排序 sort_buffer_size越小 需要分成的份数越多 number_of_tmp_files就越大
  2. 注意下 explain rows的扫描行数是1824 而我们在上面排序扫描的行数是1825 是因为建立外部排序临时表存储在磁盘上 internal_tmp_disk_storage_engine 的默认值是innoDB 查询optimizer_tarce表时 把数据从临时表取出来会把扫描行数加+1

接下来看order by的另一种排序  =====>rowid排序

上面排序算法中 存在一个问题 如果查询的数据太多 sort_buffer 中放的数据太多 这样临时文件就越多 性能会很差

mysql 配置中 max_length_for_sort_data 用于控制排序的行数 如果排序的单行超过这个值 mysql就认为单行过大 采用rowid算法

来看下rowid算法的执行流程:

1.初始化sort_buffer 放入要排序的字段和主键ID 2.走索引找到满足条件的主键ID 再通过主键ID 取出排序字段和ID 放到sort_buffer中  从索引找到下一个满足条件的主键ID 3.重复2 直到不满足条件 4.对sort_buffer中的值按照排序字段排序 5.回到原表取出所有查询的字段返回

注意一点 最后的结果集是逻辑上的概念 mysql从server段排序后的sort_buffer中取到ID 然后回到原表查其余的字段 不需要在服务端再存储的 是直接返回客户端的

我直接贴结果

可以看到sort_mode变了<sort_key,rowid>表示 参与排序只有要求排序的字段和主键ID  number_of_tmp_rows 变成了3 表示临时文件变少了 参与排序的行变小了 因此参与排序总量变小

第一种排序 vs rowid排序

mysql 认为内存不够 才会使用rowid排序 这样排序可以读取更多行 但需要回原表取一次数据  所以mysql会优先采用第一种排序

体现了mysql 设计思想 : 如果内存够 就要多利用内存 尽量减少磁盘访问

排序是个成本较高的操作 那么如果避免这个问题   从上面的过程我们可以发现mysql之所以需要生成临时表 并且在临时表上排序  是因为原来的数据都是无序的

上面的列子我们可以通过在city 和name上建立联合索引来解决 同时也可以用我们之前提到的覆盖索引来避免排序

现在分析一个问题  开发中有时候会遇到随机选取的问题 SQL可能是这样的

代码解读

复制代码

mysql> CREATE TABLE `words` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `word` varchar(64) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

select `word` from words order by rand() limit 3; 随机取出3个单词

我贴出explain 结果

Using temporary; 表示需要用临时表 上面意思就是需要的内存表上排序

问题来了 对于临时内存表来说选择那种算法

对于innoDB来说 对于内存表 回表的过程只是简单的根据数据行的位置 直接访问内存得到数据(可以理解为一个内存中的数据下标对找对应的元素) 根本不会有磁盘的访问所以优先考虑的是用于排序的行越小越好 mysql会选择rowid排序

我们来看上面那条SQL的执行流程

scss

代码解读

复制代码

    1.创建一个memory引擎的内存表 表里两个字段 一个是double字段 另一个是varchar(64)类型
    2.从Word表中 按主键顺序取出所有word的值 调用rand()函数 生成一个随机小数 存放到内存表中
    3.接下来按值排序
    4.初始化sort_buffer 里面有两个值 一个double类型 一个整型
    6 在sort_buffer中排序
    7.排序完成之后 直接选择前三个位置的数据 返回客户端

再来看个临时表不是内存表的情况 ===》转化为磁盘临时表

mysql中 'tmp_table_size' 这个配置限制了内存临时表的大小 默认是32M (mysql5.6 ) 看下图

磁盘临时表默认的引擎是 innoDB 可以用上面提到的 internal_tmp_disk_storage_engine 来控制 我们可以通过

代码解读

复制代码

1. 设置tmp_table_size 为1024kb
2. sort_buffer_size 为256k
3. max_length_for_sort_data 设置为16

接下来我直接贴出optimizer_trace的结果

从结果我们可以看到 采用的是rowid排序 但我们发现了结果中的 number_of_tmp_size 的值竟让是0 难道不需要临时文件吗???

对的确实没有用到临时文件 采用的mysql5.6 新引入的 优先队列排序算法(也就是堆排序)  我们上面的SQL只需要前面的3个有序即可 如果采用上面提到的归并排序就会多好多运算


转载来源:https://juejin.cn/post/6844903890085756942

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
11月前
|
存储 关系型数据库 MySQL
轻松入门MySQL:优化复杂查询,使用临时表简化数据库查询流程(13)
轻松入门MySQL:优化复杂查询,使用临时表简化数据库查询流程(13)
294 0
|
4月前
|
SQL 存储 缓存
MySQL执行流程
本文介绍了MySQL的执行流程,分为server层和引擎层。server层包含连接器、查询缓存、解析器、预处理器、优化器等组件,负责SQL的接收、解析、优化及执行;引擎层负责数据的存储与读取。文章详细解释了各组件的功能,如连接器负责用户身份认证,查询缓存提高查询效率,解析器进行SQL的词法和语法分析,预处理器验证表和字段的存在性,优化器选择最优执行计划,最终由查询执行引擎完成查询并将结果返回给客户端。
143 0
MySQL执行流程
|
2月前
|
SQL 存储 关系型数据库
MySQL原理简介—1.SQL的执行流程
本文介绍了MySQL驱动、数据库连接池及SQL执行流程的关键组件和作用。主要内容包括:MySQL驱动用于建立Java系统与数据库的网络连接;数据库连接池提高多线程并发访问效率;MySQL中的连接池维护多个数据库连接并进行权限验证;网络连接由线程处理,监听请求并读取数据;SQL接口负责执行SQL语句;查询解析器将SQL语句解析为可执行逻辑;查询优化器选择最优查询路径;存储引擎接口负责实际的数据操作;执行器根据优化后的执行计划调用存储引擎接口完成SQL语句的执行。整个流程确保了高效、安全地处理SQL请求。
283 77
|
3月前
|
监控 关系型数据库 MySQL
|
2月前
|
存储 SQL 缓存
MySQL原理简介—2.InnoDB架构原理和执行流程
本文介绍了MySQL中更新语句的执行流程及其背后的机制,主要包括: 1. **更新语句的执行流程**:从SQL解析到执行器调用InnoDB存储引擎接口。 2. **Buffer Pool缓冲池**:缓存磁盘数据,减少磁盘I/O。 3. **Undo日志**:记录更新前的数据,支持事务回滚。 4. **Redo日志**:确保事务持久性,防止宕机导致的数据丢失。 5. **Binlog日志**:记录逻辑操作,用于数据恢复和主从复制。 6. **事务提交机制**:包括redo日志和binlog日志的刷盘策略,确保数据一致性。 7. **后台IO线程**:将内存中的脏数据异步刷入磁盘。
166 12
|
5月前
|
存储 SQL NoSQL
|
6月前
|
SQL 搜索推荐 关系型数据库
MySQL 如何实现 ORDER BY 排序?
本文详细解析了MySQL中`ORDER BY`的实现原理及优化方法。通过解析与优化、执行及多种优化技术,如索引利用、内存排序、外部排序等,帮助你提升排序性能。了解其背后的机制,可显著优化查询效率。
412 4
|
6月前
|
SQL 搜索推荐 关系型数据库
MySQL 如何实现 ORDER BY 排序?
在实际开发中,我们经常会使用 MySQL 的 `ORDER BY`进行排序,那么,`ORDER BY`是如何实现的排序的?我们该如何优化 `ORDER BY`的排序性能?这篇文章,我们来聊一聊。
85 3
|
7月前
|
SQL 缓存 关系型数据库
揭秘MySQL一条SQL语句的执行流程
以上步骤共同构成了MySQL处理SQL语句的完整流程,理解这一流程有助于更有效地使用MySQL数据库,优化查询性能,及时解决可能出现的性能瓶颈问题。
185 7
|
8月前
|
关系型数据库 MySQL 数据处理
Mysql关于同时使用Group by和Order by问题
总的来说,`GROUP BY`和 `ORDER BY`的合理使用和优化,可以在满足数据处理需求的同时,保证查询的性能。在实际应用中,应根据数据的特性和查询需求,合理设计索引和查询结构,以实现高效的数据处理。
958 1
下一篇
oss创建bucket