MySQL 优化 —— ORDER BY 优化

引言

本文翻译自MySQL 官网:ORDER BY Optimization,MySQL 版本:5.7。

这一部分描述了MySQL何时会使用索引来满足order by子句,filesort 操作会在索引不能生效的时候被用到,以及优化器对order by的执行计划信息。

order by后面有没有跟着limit,可能会返回不同的记录顺序。

一、使用索引来满足 ORDER BY

某些情况,MySQL可能会使用索引来满足order by子句并避免因执行 filesort 操作造成的额外的排序开销。

即便order by子句并没有完全与索引匹配,可能也会用到索引,只要索引所有未使用的部分和所有额外的order by 字段在where子句中都是常量。如果索引没有包含查询的所有字段,那么只有当索引访问比其他访问方法开销更小的时候才会用到索引。

假设现在有一个索引覆盖了key_part1, key_part2这两个字段,下面的查询可能会用索引来解决order by 的部分。优化器是否真会这么做取决于如果有一个字段必须查询而它又不在索引中的情况下,读取索引是否比全表扫描更有效。

  • 在这个查询中,两个字段的复合索引可以使优化器避免排序。
SELECT * FROM t1ORDER BY key_part1, key_part2;

但是,查询使用的是select *,这可能会查询出比索引列(key_part1 , key_part2)更多的字段。这种情况,扫描整个索引并在表中记录里查找哪些不是索引列的开销可能要比扫描整个表然后对结果集排序还要大。如果这样,优化器通常就不会使用索引。如果select * 只查询了索引列,那么就会用到索引并避免排序操作。

如果表是一个InnoDB 表,那么表的主键默认也是索引的一部分,那么下面的查询就可以使用索引来解决order by:

SELECT pk, key_part1, key_part2 FROM t1ORDER BY key_part1, key_part2;
  • 在这个查询中key_part1是常量,所有通过索引访问到的记录都会按照key_part2 来排序,并且如果where子句有足够的选择性使得索引范围扫描比全表扫描开销更小的话,那么覆盖了(key_part1, key_part2)的复合索引就可以避免排序操作。
SELECT * FROM t1WHERE key_part1 = constantORDER BY key_part2;
  • 下面的两个查询,和之前没有desc的同一查询,索引的使用情况类似。
SELECT * FROM t1ORDER BY key_part1 DESC, key_part2 DESC;
SELECT * FROM t1WHERE key_part1 = constantORDER BY key_part2 DESC;
  • 下面的两个查询,key_part1 和一个常量进行比较。如果 WHERE 子句有足够的可选择性来使索引区间扫描比全表扫描更优,那么索引就会被使用。
SELECT * FROM t1WHERE key_part1 > constantORDER BY key_part1 ASC;SELECT * FROM t1WHERE key_part1 < constantORDER BY key_part1 DESC;
  • 下面的查询, ORDER BY 没有使用 key_part1,但是所有被查询的行都有一个常量的 key_part1 的值,因此依然会使用索引:
SELECT * FROM t1WHERE key_part1 = constant1 AND key_part2 > constant2ORDER BY key_part2;

某些情况,MySQL 不会使用索引来处理 ORDER BY,即便仍然会在 WHERE 子句进行匹配操作时用到索引。例如下面这些:

  • 查询中的 ORDER BY 使用了不同的 索引:
SELECT * FROM t1 ORDER BY key1, key2;
  • 查询中的 ORDER BY 使用了不连续的索引部分:
SELECT * FROM t1 WHERE key2=constant ORDER BY key1_part1, key1_part3;
  • 查询中的 ORDER BY 混用了 ASC 和 DESC:
SELECT * FROM t1 WHERE key2=constant ORDER BY key1_part1, key1_part3;
  • 用于查询记录的索引与 ORDER BY 中的索引不是同一个:
SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
  • 查询中的 ORDER BY 包含了一个表达式,这个表达式包含索引列名之外的其他项:
SELECT * FROM t1 ORDER BY ABS(key);
SELECT * FROM t1 ORDER BY -key;
  • 查询连接了多张表,并且 ORDER BY 中的字段并不都来自于第一个非常量数据表(博主:如果所有的 ORDER BY 和 GROUP BY子句中的字段都来自同一个表,那么这个表在连接时就作为第一张表。这里的意思是说,如果 ORDER BY 中的字段来自于不同的表,那么就不会用到索引)
  • 查询中包含不同的 ORDER BY 和 GROUP BY 表达式。
  • ORDER BY 子句中的字段只有字段的前缀有索引。这种情况,索引就不会完全参与排序操作。例如,有一个列声明为 CHAR(20),但是只有前10个字节建立了索引,那么索引就无法区分(博主:排序最本质的工作就是比较大小,这里的区分就是比较的意思)剩余的10个字节,这时就会使用 filesort 。
  • 索引没有以一定的顺序存储记录。例如,memory 存储引擎中用到的 HASH 索引就是这种不会对记录进行排序的索引。

另外,用于排序的索引究竟可不可用还会受到字段别名的影响。假设 t1 表的 t1.a 字段建立了索引。下面的语句中,查询列表中有这个字段 a 。它代表 t1.a ,因为 a 已经建立了索引,所以下面的语句就会用到索引:

SELECT a FROM t1 ORDER BY a;

下面的语句中,查询列表依然有 a 列,但此 a 非彼 a,这个 a 是一个别名。它代表的是 ABS(a),因为 索引是建立在 a 列上,所以 t1.a 上的索引不会生效:

SELECT ABS(a) AS a FROM t1 ORDER BY a;

下面的语句中, ORDER BY 对 a 进行排序,而在查询列表中,并没有叫 a 的列。但是 t1 表中有一个列叫做 a,因此 ORDER BY 指向的是 t1.a ,t1.a 上的索引就会生效。(当然,排序的结果可能与 ORDER BY ABS(a) 的排序完全不同)

SELECT ABS(a) AS b FROM t1 ORDER BY a;

 默认地,MySQL 会对 GROUP BY col1, col2, ... 进行排序,就好像你的查询中依然包含 ORDER BY col1, col2, ... 一样。如果你显式地包含一个 ORDER BY  子句,包含了相同的字段列表,MySQL 会把它优化掉,排序依然会发生,且不会有性能牺牲。

如果一个查询包含 GROUP BY 但是你想避免对结果排序的开销。你可以通过指定 ORDER BY NULL 来抑制排序。例如:

INSERT INTO foo
SELECT a, COUNT(*) FROM bar GROUP BY a ORDER BY NULL;

优化器可能依然会用排序来实现分组操作。ORDER BY NULL 抑制了对结果的排序,分组操作之前不会有排序来决定结果。

注意:

GROUP BY 默认隐式排序(即 GROUP BY 的字段没有 ASC 或 DESC)。然而,依赖隐式 GROUP BY 排序或显式 GROUP BY (即使用声明的 ASC 或 DESC 来对 GROUP BY 的字段排序)排序都是不推荐的。要排序,请用 ORDER BY 子句。

二、 使用 filesort 来满足 ORDER BY

如果索引已经无法满足 ORDER BY 子句,MySQL 会执行 filesort 操作,它的意思是——读取表中数据然后排序。filesort 会在查询执行的时候有额外的排序时间。

为了获取用于 filesort 操作的内存,优化器会预先分配一个固定大小为 sort_buffer_size 个字节。每一个session 会话可以通过改变这个值来避免过度的内存消耗,或者在必要时分配更多内存。

如果结果集真的大到内存已经无法装下,那么 filesort 操作会在这种必要的时候使用临时的磁盘文件。有些类型的查询尤其适合完成内存内的 filesort 操作。例如,下面的查询形式,优化器就可以使用 filesort 在内存中,而不是使用临时文件来高效地处理 ORDER BY 操作:

SELECT ... FROM single_table ... ORDER BY non_index_column [DESC] LIMIT [M,]N;

下面的查询在 web 应用中非常常见,它们用于让一个很大的结果集展示很少的一些记录。例如:

SELECT col1, ... FROM t1 ... ORDER BY name LIMIT 10;
SELECT col1, ... FROM t1 ... ORDER BY RAND() LIMIT 15;

三、影响 ORDER BY 优化

对于连 filesort 都无法生效的慢查询,尝试把 max_length_for_sort_data 系统变量调低到一个适合触发 filesort 的值。(一个该值设置地太高的症状是系统同时会出现高磁盘活动和低CPU 活动

为了加快 ORDER BY 的速度,检查一下你是否可以使用索引而不是额外的排序时间(博主:即使用 filesort)。如果无法做到,尝试下面的优化策略:

  • 调大 sort_buffer_size 变量的值。理想状态下,这个值应该大到足够整个结果集可以容纳在排序缓冲区(为了避免磁盘写入和合并次数),但是该值最小也必须要足够容纳15个元组。(合并最多15个临时磁盘文件,每个文件必须有至少一个元组的内存空间)。
  • 考虑到存储在排序缓冲区中的列值的大小受到 max_sort_length 系统变量的影响。例如,如果元组存储长字符串字段的值,并且你调大了 max_sort_length 的值,那么排序缓冲区元组的大小也会变大,并且你可能同时需要增大 sort_buffer_size 的大小。对于作为字符串表达式结果计算的列值(例如那些需要一个字符串参数的函数),filesort 算法无法得知表达式的最大长度,所以就一定会给每个元组分配 max_sort_length 的字节数。如果要监控合并次数(合并临时文件),可以检查 Sort_merge_passes 状态变量。
  • 调大 read_rnd_buffer_size 变量可以在同一时间读取更多的记录。
  • 改变 tmpdir 系统变量的值,使其指向一个具有大量空闲空间的专用文件系统。该变量值会列出几个以循环方式使用的路径。使用这个功能可以让负载分散到多个目录。在Unix 系统上以冒号(:)分隔路径,在 Windows 系统上以分号(;)分隔路径。路径应该命名位于不同物理磁盘上的文件系统中的目录,而不是同一磁盘上的不同分区。

四、ORDER BY 的执行计划信息

使用 EXPLAIN ,你可以检查MySQL 是否有用到索引来解决 ORDER BY 子句:

  • 如果在输出的执行计划中 Extra 列不包含 "Using filesort" ,那么索引排序生效,filesort 不会执行。
  • 如果在输出的执行计划中 Extra 列包含 "Using filesort",那么排序就没有用到索引,filesort 就会执行。

另外,如果 filesort 执行了,优化器跟踪输出包括一个 filesort_summary 块,例如:

"filesort_summary": {"rows": 100,"examined_rows": 100,"number_of_tmp_files": 0,"sort_buffer_size": 25192,"sort_mode": "<sort_key, packed_additional_fields>"
}

sort_mode 值提供了一些关于排序缓冲区中的元组的信息。

  • <sort_key, rowid> :它表示排序缓冲区元组是包含原表行的排序键值和行ID的对。元组被排序键排序,记录 ID 被用于从表中读取数据。
  • <sort_key, additional_fields>:它表示排序缓冲区元组包含排序键值和查询涉及到的字段。元组被排序键排序,字段值会直接从元组中读取。
  • <sort_key, packed_additional_fields>:与前面的变体类似,但是附加的列是紧密地打包在一起的,而不是使用固定长度的编码。

EXPLAIN 不会分辨优化器是否会执行内存中的 filesort 。内存 filesort 的使用可以在优化器的跟踪报告中看到。

总结

关于 ORDER BY 的优化部分,的确是非常复杂,其中比较重要的是关于 ORDER BY 如何利用索引的具体条件。在某些场合下,ORDER BY 是不会使用到索引的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558710.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL 高级 —— 索引实现的思考

引言 最近看了一个公开课&#xff0c;是有关MySQL对索引设计的思考。详细讲解了几种索引实现的设计思考与利弊辨析&#xff0c;讨论了为什么MySQL默认情况下会使用B树索引&#xff0c;B树索引又对B树做了哪些结构改进。 本片博客通过个人的学习理解和总结&#xff0c;由几种简…

MySQL 优化 —— MySQL 如何使用索引

引言 本文翻译自MySQL 官网 &#xff1a;How MySQL Uses Indexes &#xff0c;MySQL 版本 5.7 。 提升 SELECT 操作性能最好的方式就是在查询的一列或多列上建立索引。索引的行为类似指向表数据的指针&#xff0c;可以让查询能够快速判断哪个记录满足 WHERE 子句中的条件&…

MySQL 优化 —— EXPLAIN 执行计划详解

引言 本博客大部分内容翻译自MySQL 官网 Understanding the Query Execution Plan 专题。另外有一些补充&#xff0c;则来自于网课以及《高性能MySQL&#xff08;第三版&#xff09;》。 根据我们的表、字段、索引、以及 where 子句中的条件等信息&#xff0c;MySQL 优化器会…

Git 初学札记(十)—— Reset 回退的三种状态解析

引言 工作中经常会涉及到需要本地代码覆盖更新的操作。有时候可能是从远端git 上直接覆盖更新&#xff0c;或者是其他本地分支覆盖更新当前分支等等。这个时候就需要用到 reset 操作。 reset 操作分为三种类型&#xff1a;Soft、Mixed、Hard。今天我们就来说说这三种类型究竟…

MySQL 高级 —— 深入理解 InnoDB 与 MyISAM

引言 在文件系统中&#xff0c;MySQL将每个数据库&#xff08;也可以称之为schema&#xff09;保存为数据目录下的一个子目录。创建表时&#xff0c;MySQL会在数据库子目录下创建一个与表同名的.frm文件保存表的定义。因为MySQL使用文件系统的目录和文件来保存数据库和表的定义…

Ts声明ElementUI控件

初用Ts&#xff0c;有时候想获取三方控件不太会声明类型&#xff0c;记录一下使用InstanceType导入类型 例如声明一个el-select <el-form-item label"类型:" prop"year" :loading"state.loading"><el-select v-model"props.ruleF…

关于 OutOfMemoryError 的总结与解决方法

引言 本文总结自周志明的《深入理解Java虚拟机》第二章部分内容。 这部分内容&#xff0c;可以为后续性能调优方面的工作起到铺垫作用。 一、什么是 OutOfMemoryError OurOfMemory 简称“OOM”&#xff0c; 直译为“内存耗尽”或“内存溢出”&#xff0c;当然&#xff0c;并…

Windows误关闭资源管理器重启的办法

引言 有时候Windows系统在开机后&#xff0c;在桌面底部的任务栏中无法正常加载必要的网络连接图标或音量图标等&#xff0c;导致无法手动操作音量或连接网络。这时候就会需要打开“任务管理器”重新启动“资源管理器”使其重新加载这些必要的控制图标。 但是由于操作失误&am…

MySQL高级 —— 高性能索引

引言 最近一直在抱着《高性能MySQL&#xff08;第三版&#xff09;》研究MySQL相关热点问题&#xff0c;诸如索引、查询优化等&#xff0c;这阶段的学习是前一段时间MySQL基础与官方的“阅读理解”的进一步延伸。 书中第五章详细阐述了如何设计高性能的索引&#xff0c;以及索…

MySQL高级 —— 查询性能优化

引言 承接《MySQL高级 —— 高性能索引》&#xff0c;本篇博客将围绕《高性能MySQL&#xff08;第三版&#xff09;》第六章内容进行总结和概括。 与索引的部分一样&#xff0c;SQL优化也是广大程序员深入MySQL的又一条必经之路。希望通过本篇博客的总结&#xff0c;能够为我…

哈希表的大小为何最好是素数

引言 为什么散列函数采用取模运算&#xff1f;又为什么取模运算的被取模数最好是素数&#xff1f;素数是如何在取模运算中很好的规避冲突的&#xff1f; 这些问题可能困扰诸多程序员很久了。我们总是说素数可以更好的避免冲突&#xff0c;但总是对各种长篇大论的分析望而却步…

Java常用设计模式————适配器模式

引言 由于无法直接使用某个类中的方法而采取的一种中间类转换的策略。将一个类的接口转换成另一个接口&#xff0c;让原本接口不兼容的类可以兼容。 适配器模式可以分为三种&#xff1a;类适配器、对象适配器、接口适配器。它们之间的区别主要体现在适配器角色与被适配角色之…

Java常用设计模式————桥接模式

引言 在实际的业务中&#xff0c;经常会遇到多维度的概念组合&#xff0c;公园的门票&#xff0c;颐和园有年票、月票、日票&#xff0c;故宫也有年票、月票、日票。那么不同的公园和票种类型就可以视为两种不同的纬度&#xff0c;它们之间会形成相互组合的关系。 在类的设计…

Java常用设计模式————装饰者模式

引言 装饰者模式&#xff0c;又叫装饰器模式。它可以动态的将新功能附加到对象上。在对象功能扩展方面&#xff0c;它比继承更灵活&#xff0c;同时装饰者模式也体现了OCP原则。 在客户端调用使用了装饰者模式的对象时&#xff0c;就好像在使用构造器层层包裹核心对象&#x…

Java常用设计模式————组合模式

引言 组合模式&#xff0c;是一种类似递归算法的结构性设计模式&#xff0c;通过以简单的 List &#xff0c;组合本类对象&#xff0c;实现树状对象结构的“部分、整体”的层次。 它可以让调用程序不需要关心复杂对象与简单对象的区别&#xff0c;而统一地实现处理逻辑。 对…

MySQL 高级 —— MVCC 多版本并发控制

引言 MySQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑&#xff0c;它们一般都同时实现了多版本并发控制——MVCC。包括其他数据库如Oracle等&#xff0c;由于MVCC并没有一个统一的实现标准&#xff0c;因此它们的实现原理都不尽相同。 MVCC简介…

Java常用设计模式————外观模式

引言 外观模式&#xff08;Facade Pattern&#xff09;&#xff0c;又叫“过程模式”。外观模式为子系统中的一组接口提供一个一致的入口&#xff0c;此模式定义了一个高层接口&#xff0c;这个接口使得这一组子系统更加易用。 一、案例分析 生活中有很多类似的案例&#xf…

Java常用设计模式————享元模式

引言 享元模式&#xff0c;也叫蝇量模式&#xff08;Flyweight Pattern&#xff09;。运用共享技术有效地支持大量细粒度的对象。 享元模式常用于系统底层开发&#xff0c;解决系统的性能问题。例如数据库连接池&#xff0c;里面都是创建好的连接对象&#xff0c;在这些连接对…

IDEA——常用基础设置

一、设置入口 File—>Settings... 或者 在工具栏的“小扳手”图标。 二、主题设置 三、编辑通用设置 设置面板中的 Editor 3.1 自动导包 可以设置IDEA自动为程序导包&#xff0c;在书写时加入准确的导包&#xff0c;在书写时优化导包&#xff08;自动去掉未使用的&#…

IDEA——常用快捷键

引言 总结 IDEA 的常用快捷键&#xff0c;除了部分快捷键与 Eclipse 保持一致之外&#xff0c;枚举更多的实用快捷键。 一、如何设置快捷键 在 Settings -> Keymap 中&#xff0c;下拉框里选择 Eclipse &#xff0c;即可将 IDEA 的快捷键设置为与 Eclipse 保持一致。但并…