当前位置: 首页 > news >正文

阅读MySQL实战45讲第11天

目录

引言:

基本原理

排序方式

索引排序

文件排序(File Sort)

优化建议

一、全字段排序

二、rowid 排序

如果 MySQL 认为排序的单行长度太大会怎么做呢?

1. 使用磁盘临时表

2. 分阶段排序

3. 产生警告或错误

三、全字段排序 VS rowid 排序

MySQL 排序算法选择策略

MySQL 设计思想


引言:

在 MySQL 里,ORDER BY 子句用于对查询结果进行排序,下面从基本原理、排序方式以及优化方面为你介绍它的工作机制。

基本原理

  • 扫描数据执行 ORDER BY 时,MySQL 首先会根据查询条件从表中扫描出符合条件的数据行。
  • 排序操作将扫描出的数据按照 ORDER BY 子句指定的列和排序规则(升序 ASC 或降序 DESC)进行排序。排序操作可以在内存或磁盘上进行,具体取决于数据量大小和可用内存。
  • 返回结果:排序完成后,MySQL 将排序好的数据作为查询结果返回。

排序方式

索引排序
  • 原理:若 ORDER BY 子句中的列存在索引,MySQL 会优先使用索引进行排序。因为索引本身就是有序的,所以可以直接从索引中获取有序的数据,避免了额外的排序操作。
  • 示例
在这个例子中,age 列上有索引,MySQL 可以直接利用该索引进行排序,提高查询效率。
文件排序(File Sort)
  • 原理:当无法使用索引进行排序时,MySQL 会采用文件排序算法。它会将需要排序的数据读取到 sort_buffer 中进行排序,如果数据量超过 sort_buffer 的大小,会将数据分成多个临时文件,分别排序后再合并成一个有序的结果。
  • 示例

优化建议

  • 创建合适的索引:为 ORDER BY 子句中的列创建索引,以提高排序效率。但要注意避免创建过多的索引,以免影响插入、更新和删除操作的性能。
  • 优化查询语句:尽量减少 SELECT 子句中返回的列,避免不必要的列参与排序。可以使用覆盖索引,即索引包含了查询所需的所有列,这样可以直接从索引中获取数据,避免回表操作。
  • 调整系统参数:适当调整 sort_buffer_size 参数,增大排序缓冲区的大小,以减少磁盘 I/O 操作。但要注意不要设置得过大,以免占用过多的内存资源。

通过以上的阐述,一起了解一下order by是如何工作的?聊聊以上语句是怎么执行的,以及有什么参数会影响执行的行为。

一、全字段排序

前面我们介绍过索引,所以你现在就很清楚了,为避免全表扫描,我们需要在 city 字段加上索引。
在 city 字段上创建索引之后,我们用 explain 命令来看看这个语句的执行情况。
                                     图 1 使用 explain 命令查看语句的执行情况
Extra 这个字段中的“Using filesort”表示的就是需要排序,MySQL 会给每个线程分配一块内存用于排序,称为 sort_buffer。
为了说明这个 SQL 查询语句的执行过程,我们先来看一下 city 这个索引的示意图。
                                                    图 2 city 字段的索引示意图
从图中可以看到,满足 city='杭州’条件的行,是从 ID_X 到 ID_(X+N) 的这些记录。
通常情况下,这个语句执行流程如下所示 :
1. 初始化 sort_buffer,确定放入 name、city、age 这三个字段;
2. 从索引 city 找到第一个满足 city='杭州’条件的主键 id,也就是图中的 ID_X;
3. 到主键 id 索引取出整行,取 name、city、age 三个字段的值,存入 sort_buffer 中;
4. 从索引 city 取下一个记录的主键 id;
5. 重复步骤 3、4 直到 city 的值不满足查询条件为止,对应的主键 id 也就是图中的ID_Y;
6. 对 sort_buffer 中的数据按照字段 name 做快速排序;
7. 按照排序结果取前 1000 行返回给客户端。
暂且把这个排序过程,称为 全字段排序
形象总结这个流程:就是通过一个毛线球的引线,去找到喜爱的花色,然后再用喜爱的花色织毛衣的时候,发现不够了,再去接着找。
执行流程的示意图如下所示:
                                                                图 3 全字段排序
图中“按 name 排序”这个动作,可能在内存中完成,也可能需要使用外部排序,这取决于排序所需的内存和参数 sort_buffer_size。
sort_buffer_size,就是 MySQL 为排序开辟的内存(sort_buffer)的大小。如果要排序的数据量小于 sort_buffer_size,排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序。 (小孩自己能解决的问题就自己解决,自己解决不了就找大人)
如果 sort_buffer_size 超过了需要排序的数据量的大小,number_of_tmp_files 就是 0,表示排序可以直接在内存中完成。
否则就需要放在临时文件中排序。sort_buffer_size 越小,需要分成的份数越多,number_of_tmp_files 的值就越大。

额外补充:MySQL 将需要排序的数据分成 12 份,每一份单独排序后存在这些临时文件中。然后把这 12 个有序文件再合并成一个有序的大文件。

MySQL 中这种将需要排序的数据分成多份,分别排序后再合并的原理是利用了 分治策略归并排序的思想,具体如下:
  • 分治策略
    • 先将大规模的排序问题分解成若干个小规模的排序问题,即将需要排序的数据分成 12 份,这样可以降低每个子问题的处理难度。因为对较小规模的数据进行排序相对容易,并且可以在内存中更高效地完成。
    • 对这 12 份数据分别进行单独排序,得到 12 个有序的临时文件。此时每个临时文件中的数据都是局部有序的。
  • 归并排序思想
    • 归并排序是一种稳定的排序算法,其核心思想是将两个或多个有序序列合并成一个有序序列。在这个场景中,就是将 12 个有序的临时文件合并成一个有序的大文件。
    • 合并过程通常使用多路归并的方法。它会同时读取这 12 个有序文件的开头部分,比较它们的键值大小,然后将最小(或最大,根据排序要求)的键值及其对应的数据放入最终的有序大文件中。接着,从相应的临时文件中读取下一个数据,继续比较和合并,直到所有临时文件中的数据都被合并到大文件中。
通过这种分而治之再合并的方式,MySQL 能够处理大规模的数据排序,避免了一次性对大量数据进行排序时可能出现的内存不足等问题,提高了排序的效率和可扩展性。

二、rowid 排序

如果 MySQL 认为排序的单行长度太大会怎么做呢?

1. 使用磁盘临时表

通常情况下,MySQL 优先尝试在内存中进行排序操作,会使用内存临时表。但当单行长度过大,内存临时表无法容纳时,MySQL 会转而使用磁盘临时表。
磁盘临时表是将数据存储在磁盘上的临时文件,这会带来一定的性能开销,因为磁盘的读写速度远低于内存。例如,在处理包含大量文本字段或者 blob、text 类型数据的排序时,就可能出现这种情况。

2. 分阶段排序

  • 外部排序:当数据无法全部放入内存进行排序时,MySQL 会采用外部排序算法。它会将数据分成多个较小的块,这些块可以分别在内存中排序,排序后将结果存储在磁盘上的临时文件中。之后,再将这些有序的临时文件合并成一个最终的有序结果。
  • 多趟排序:如果单行长度大导致内存只能容纳少量数据行,MySQL 可能需要进行多趟排序。每一趟排序处理一部分数据,然后逐步合并这些部分排序的结果。

3. 产生警告或错误

如果排序操作因为单行长度过大而无法正常进行,MySQL 可能会产生警告或错误信息。比如,当磁盘空间不足,无法创建磁盘临时表时,就会抛出错误,导致排序操作失败。

示例

假设你有一个表 large_table,其中包含一个text类型的字段 long_text,当你尝试对这个字段进行排序时,可能会触发上述机制。
在执行这个查询时,如果  long_text 字段中的数据过长,导致单行长度超出了 MySQL 内存临时表的限制,MySQL 就会切换到磁盘临时表进行排序。你可以通过查看 MySQL 的错误日志或者使用  SHOW WARNINGS; 语句来查看是否有相关的警告信息。
图 4 rowid 排序
对比图 3 的全字段排序流程图你会发现,rowid 排序多访问了一次表 t 的主键索引,就是步骤 7。
最后的“结果集”是一个逻辑概念,实际上 MySQL 服务端从排序后的sort_buffer 中依次取出 id,然后到原表查到 city、name 和 age 这三个字段的结果,不需要在服务端再耗费内存存储结果,是直接返回给客户端的。

三、全字段排序 VS rowid 排序

如果 MySQL 实在是担心排序内存太小,会影响排序效率,才会采用 rowid 排序算法,这样排序过程中一次可以排序更多行,但是需要再回到原表去取数据。
如果 MySQL 认为内存足够大,会优先选择全字段排序,把需要的字段都放到 sort_buffer中,这样排序后就会直接从内存里面返回查询结果了,不用再回到原表去取数据。
这也就体现了 MySQL 的一个设计思想: 如果内存够,就要多利用内存,尽量减少磁盘访问。
对于 InnoDB 表来说,rowid 排序会要求回表多造成磁盘读,因此不会被优先选择。

MySQL 排序算法选择策略

  • rowid 排序:当 MySQL 担心排序内存太小,影响排序效率时,会采用 rowid 排序算法。该算法一次可以排序更多行,它先根据 rowid(在 InnoDB 中,如果表没有显式定义主键,会有一个隐藏的 rowid 用于唯一标识行)对数据进行排序,排序完成后再根据 rowid 回到原表中获取所需的其他字段数据。由于需要回表操作,会造成额外的磁盘读,所以通常情况下不是优先选择的排序方式。
  • 全字段排序:当 MySQL 认为内存足够大时,会优先选择全字段排序。会把查询所需的所有字段都放到 sort_buffer 中进行排序,排序完成后可以直接从内存中返回查询结果,避免了回表操作,从而减少了磁盘访问,提高了查询效率。

MySQL 设计思想

从 MySQL 对这两种排序算法的选择可以看出其设计思想是:如果内存充足,就充分利用内存来存储和处理数据, 尽量减少对磁盘的访问。因为磁盘的读写速度远慢于内存,减少磁盘访问可以显著提高数据库的性能和查询效率。通过根据不同的内存情况选择合适的排序算法,MySQL 能够在各种环境下尽可能地优化查询性能,以适应不同的业务需求和数据量规模
http://www.xdnf.cn/news/194635.html

相关文章:

  • uniapp打包apk如何实现版本更新
  • Spring MVC异常处理利器:深入理解HandlerExceptionResolver
  • SpringBoot实现接口防刷的5种高效方案详解
  • C#/.NET/.NET Core技术前沿周刊 | 第 36 期(2025年4.21-4.27)
  • AudioSet 音频中文类别
  • 蚂蚁seo蜘蛛池:提升网站收录的秘密武器
  • Nacos源码—1.Nacos服务注册发现分析二
  • 系统思考提升培训效能
  • 100天精通Python挑战总览 | 零基础到应用实战!
  • 安徽地区安全员A证考试中,哪些知识点是高频考点?
  • mysql8.0版本部署+日志清理+rsync备份策略
  • LLaMA-Factory部署以及大模型的训练(细节+新手向)
  • 基于 Java 的实现前端组装查询语句,后端直接执行查询方案,涵盖前端和后端的设计思路
  • Vue组件开发进阶:从通信原理到DOM异步更新实战
  • 在视图中交互 闪退问题
  • 1. 用户之窗
  • 【阿里云大模型高级工程师ACP习题集】2.6.用插件扩展答疑机器人的能力边界
  • IEC 61850标准协议解读 2.基于Java的MMS实现
  • Python爬虫实战:获取猫yan电影网最新热门电影数据并做分析,为51观影做参考
  • ArcGIS arcpy代码工具——根据属性结构表创建shape图层
  • OpenGL----OpenGL纹理与纹理缓存区
  • ICH CTD中ISS的关键内容与作用
  • ubuntu新增磁盘挂载
  • 如何将现有资源导入到 Terraform 管理?
  • BT131-ASEMI无人机专用功率器件BT131
  • 【更新】LLM Interview (2)
  • [特殊字符] 基于Docker部署Nacos注册中心及微服务注册发现详解(含MySQL持久化配置)
  • Android常见仓库与国内仓库对应关系
  • MaxScript二维图形布尔(并)运算
  • Hadoop和Spark大数据挖掘与实战