添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
本节描述了 MySQL 何时可以使用索引来满足 ORDER BY 子句,无法使用索引时使用的 filesort 操作,以及优化器提供的执行计划信息 ORDER BY

ORDER BY with 和 without 以 LIMIT 不同的顺序返回行,如 第 8.2.1.19 节“LIMIT 查询优化” 中所述。

BY索引与索引不完全匹配,也可以使用索引,只要索引的所有未使用部分和所有额外 ORDER BY 列在 WHERE 子句中都是常量。如果索引不包含查询访问的所有列,则仅当索引访问比其他访问方法便宜时才使用索引。 假设 上有一个索引 ,下面的查询可能会使用该索引来解析该 部分。如果还必须读取不在索引中的列,优化器是否实际这样做取决于读取索引是否比表扫描更有效。 ( key_part1 , key_part2 ) ORDER BY

在此查询中,索引 on 使优化器能够避免排序: ( key_part1 , key_part2 )

SELECT * FROM t1
  ORDER BY key_part1, key_part2;

但是,查询使用 SELECT * , 可能会选择比 key_part1 和 更多的列 key_part2 。在这种情况下,扫描整个索引并查找表行以查找不在索引中的列可能比扫描表并对结果排序更昂贵。如果是这样,优化器可能不使用索引。如果 SELECT * 仅选择索引列,则使用索引并避免排序。 如果 t1 是一个 InnoDB 表,表主键是索引的隐式部分,索引可用于解决 ORDER BY 此查询:

SELECT pk, key_part1, key_part2 FROM t1
  ORDER BY key_part1, key_part2;
  • 在此查询中, key_part1 是常量,因此通过索引访问的所有行都是 key_part2 有序的,如果子句的选择性足以使索引范围扫描比表扫描更便宜, 则索引 on 会避免排序: ( key_part1 , key_part2 ) WHERE

    SELECT * FROM t1
      WHERE key_part1 = constant
      ORDER BY key_part2;
  • 在接下来的两个查询中,是否使用索引与 DESC 之前未显示的相同查询类似:

    SELECT * FROM t1
      ORDER BY key_part1 DESC, key_part2 DESC;
    SELECT * FROM t1
      WHERE key_part1 = constant
      ORDER BY key_part2 DESC;
  • an 中的两列 ORDER BY 可以按相同方向(两个 ASC ,或两个 DESC )或相反方向(一个 ASC ,一个 DESC )排序。使用指数的一个条件是指数必须具有相同的同质性,但不必具有相同的实际方向。 如果查询混合使用 ASC DESC ,如果索引也使用相应的混合升序和降序列,则优化器可以在列上使用索引:

    SELECT * FROM t1
      ORDER BY key_part1 DESC, key_part2 ASC;

    如果降序和 优化器可以使用 ( key_part1 , key_part2 ) 上的索引。如果是升序和降序,它还可以在这些列上使用索引(通过向后扫描)。请参阅 第 8.3.13 节,“降序索引” key_part1 key_part2 key_part1 key_part2

  • 在接下来的两个查询中, key_part1 与常量进行比较。如果该 WHERE 子句的选择性足以使索引范围扫描比表扫描更便宜,则使用该索引:

    SELECT * FROM t1
      WHERE key_part1 > constant
      ORDER BY key_part1 ASC;
    SELECT * FROM t1
      WHERE key_part1 < constant
      ORDER BY key_part1 DESC;
  • 在下一个查询中, ORDER BY 没有 name key_part1 ,但是所有选择的行都有一个常 key_part1 量值,所以索引仍然可以使用:

    SELECT * FROM t1
      WHERE key_part1 = constant1 AND key_part2 > constant2
      ORDER 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

    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 并非全部来自用于检索行的第一个非常量表。(这是 EXPLAIN 输出中第一个没有 const 连接类型的表。) 查询具有不同的 ORDER BY and GROUP BY 表达式。

  • ORDER BY 仅在子句 中命名的列的前缀上有一个索引。在这种情况下,索引不能用于完全解析排序顺序。例如,如果仅对 CHAR(20) 列的前 10 个字节建立索引,则索引无法区分超过第 10 个字节的值, filesort 因此需要 a。 索引不按顺序存储行。例如,这适用 HASH 于表中的索引 MEMORY 。 用于排序的索引的可用性可能会受到使用列别名的影响。假设该列 t1.a 已建立索引。在此语句中,选择列表中列的名称是 a 。它指的是 t1.a ,对 的引用 a 也是 如此,因此可以使用 ORDER BY 索引 on : t1.a

    SELECT a FROM t1 ORDER BY a;

    在该语句中,选择列表中的列的名称也是 a ,但它是别名。它 ABS(a) 和 中的引用一样引用 a ,因此不能使用 ORDER BY 索引: t1.a

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

    在下面的语句中, the ORDER BY 指的是一个不是选择列表中列名的名称。 t1 但是named中有一个列 a ,所以可以使用 ORDER BY 引用 t1.a 和索引。 t1.a (当然,生成的排序顺序可能与 的顺序完全不同 ABS(a) 。)

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

    以前(MySQL 5.7 及更低版本) GROUP BY 在某些条件下隐式排序。在 MySQL 8.0 中,不再发生这种情况,因此不再 ORDER BY NULL 需要在末尾指定抑制隐式排序(如之前所做的那样)。但是,查询结果可能与以前的 MySQL 版本不同。要生成给定的排序顺序,请提供一个 ORDER BY 子句。 filesort 读取表行并对它们进行排序的操作。A filesort 构成查询执行中的额外排序阶段。 为了获得用于 filesort 操作的内存,从 MySQL 8.0.12 开始,优化器根据需要递增地分配内存缓冲区,直到系统变量指示的大小 ,而不是像 MySQL 8.0 之前那样预先 sort_buffer_size 分配固定数量的 字节 sort_buffer_size .12. 这使用户能够设置 sort_buffer_size 更大的值来加速更大的排序,而不用担心小排序会使用过多的内存。(对于多线程较弱的 Windows 上的多个并发排序可能不会出现此好处 malloc 。) 如果 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;
    ,请尝试将 系统变量降低到适合触发 . (将此变量的值设置得太高的症状是高磁盘活动和低 CPU 活动的组合。)此技术仅适用于 MySQL 8.0.20 之前。从 8.0.20 开始, 由于优化器更改使其过时且无效,因此已弃用。 filesort max_length_for_sort_data filesort max_length_for_sort_data

    要提高 ORDER BY 速度,请检查是否可以让 MySQL 使用索引而不是额外的排序阶段。如果这不可能,请尝试以下策略: sort_buffer_size 变量值。理想情况下,该值应该足够大,以便整个结果集适合排序缓冲区(以避免写入磁盘和合并过程)。 考虑到存储在排序缓冲区中的列值的大小受 max_sort_length 系统变量值的影响。例如,如果元组存储长字符串列的值并且您增加 的值 max_sort_length ,则排序缓冲区元组的大小也会增加并且可能需要您增加 sort_buffer_size 。 要监视合并遍数(合并临时文件),请检查 Sort_merge_passes 状态变量。 read_rnd_buffer_size 变量值以便一次读取更多行。 更改 tmpdir 系统变量以指向具有大量可用空间的专用文件系统。变量值可以列出多个以循环方式使用的路径;您可以使用此功能将负载分散到多个目录中。 : 在 Unix 上用冒号字符 ( ) 分隔路径, ; 在 Windows 上用分号字符 ( ) 分隔路径。路径应该命名位于不同 物理 磁盘上的文件系统中的目录,而不是同一磁盘上的不同分区。 "number_of_tmp_files": 0, "peak_memory_used": 25192, "sort_mode": "<sort_key, packed_additional_fields>" peak_memory_used 指示在排序过程中任何一次使用的最大内存。这是一个最大但不一定与 sort_buffer_size 系统变量值一样大的值。在 MySQL 8.0.12 之前,输出显示 sort_buffer_size 的是 sort_buffer_size . (在 MySQL 8.0.12 之前,优化器总是 sort_buffer_size 为排序缓冲区分配字节。从 8.0.12 开始,优化器逐渐分配排序缓冲区内存,从少量开始,然后根据需要添加更多,直到 sort_buffer_size 字节。) 该 sort_mode 值提供有关排序缓冲区中元组内容的信息: <sort_key, rowid> :这表示排序缓冲区元组是包含原始表行的排序键值和行ID的对。元组按排序键值排序,行 ID 用于从表中读取行。 <sort_key, additional_fields> :这表示排序缓冲区元组包含查询引用的排序键值和列。元组按排序键值排序,列值直接从元组中读取。 <sort_key, packed_additional_fields> :与之前的变体一样,但是附加的列被紧密地打包在一起,而不是使用固定长度的编码。 EXPLAIN 不区分优化器是否 filesort 在内存中执行。 filesort 在优化器跟踪输出中可以看到内存中的使用 filesort_priority_queue_optimization 。有关优化器跟踪的信息,请参阅 MySQL 内部结构:跟踪优化器

  •