Aurora MySQL 中用于并行查询的 SQL 构造 - Amazon Aurora

Aurora MySQL 中用于并行查询的 SQL 构造

在下一节中,您可以找到为什么特定 SQL 语句使用或不使用并行查询的更多详细信息。本节还详细介绍了 Aurora MySQL 功能如何与并行查询交互。此信息可以帮助您诊断使用并行查询的集群的性能问题,或了解并行查询如何应用于您的特定工作负载。

确定是否使用并行查询取决于在运行语句时存在的很多因素。因此,某些查询可能始终、从不或仅在某些情况下使用并行查询。

提示

当您在 HTML 中查看这些示例时,可以使用每个代码列表右上角的复制小部件来复制 SQL 代码以便自行尝试。使用复制小部件可避免复制 mysql> 提示行和 -> 延续行周围的多余字符。

EXPLAIN 语句

正如本节中的示例所示,EXPLAIN 语句指示查询的每个阶段当前是否适合运行并行查询。它还指示可以将查询的哪些方面向下推送到存储层。查询计划中的最重要项目如下所示:

  • NULL 列的 key 以外的值表明可以使用索引查找高效地执行查询,而不会运行并行查询。

  • 较小的 rows 列值(不是数百万的值)表明查询没有访问足够的数据以值得运行并行查询。这意味着不太可能使用并行查询。

  • Extra 列显示是否需要使用并行查询。该输出类似于以下示例。

    Using parallel query (A columns, B filters, C exprs; D extra)

    columns 数字表示查询块中引用的列数。

    filters 数字表示 WHERE 谓词数,它表示列值与常数的简单比较。比较的结果可以是相等、不相等,或者是处于某个范围内。Aurora 可以十分高效地并行处理此类谓词。

    exprs 数字表示也可以并行处理但不像筛选条件那样高效的表达式数,例如,函数调用、运算符或其他表达式。

    extra 数字表示无法向下推送并由头节点执行的表达式数。

例如,请考虑以下 EXPLAIN 输出。

mysql> explain select p_name, p_mfgr from part -> where p_brand is not null -> and upper(p_type) is not null -> and round(p_retailprice) is not null; +----+-------------+-------+...+----------+----------------------------------------------------------------------------+ | id | select_type | table |...| rows | Extra | +----+-------------+-------+...+----------+----------------------------------------------------------------------------+ | 1 | SIMPLE | part |...| 20427936 | Using where; Using parallel query (5 columns, 1 filters, 2 exprs; 0 extra) | +----+-------------+-------+...+----------+----------------------------------------------------------------------------+

Extra 列中的信息显示从每行中提取 5 列以计算查询条件并构建结果集。一个 WHERE 谓词涉及一个筛选条件,即,在 WHERE 子句中直接测试的列。两个 WHERE 子句需要计算更复杂的表达式,在这种情况下,将涉及函数调用。0 extra 字段确认 WHERE 子句中的所有操作将作为并行查询处理的一部分向下推送到存储层。

如果未选择并行查询,您通常可以从 EXPLAIN 输出的其他列中推断出原因。例如,rows 值可能太小,或者 possible_keys 列可能表示查询可以使用索引查找,而不是数据密集型扫描。以下示例显示了一个查询,其中优化程序可以估计查询将仅扫描少量的行。它根据主键的特性执行此操作。在这种情况下,不需要运行并行查询。

mysql> explain select count(*) from part where p_partkey between 1 and 100; +----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+ | 1 | SIMPLE | part | range | PRIMARY | PRIMARY | 4 | NULL | 99 | Using where; Using index | +----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+

显示是否使用并行查询的输出考虑了在运行 EXPLAIN 语句时存在的所有因素。在实际运行查询时,如果在此期间情况发生变化,优化程序可能会做出不同的选择。例如,EXPLAIN 可能会报告语句将使用并行查询。但在以后实际运行查询时,它可能会根据此时的情况不使用并行查询。此类条件可以包括同时运行其他几个并行查询。此类情况还可能包括从表中删除行,创建新的索引,在打开事务中经过的时间太长,等等。

WHERE 子句

要使查询使用并行查询优化,它必须 包含一个 WHERE 子句。

并行查询优化加快了 WHERE 子句中使用的多种类型的表达式的速度:

  • 列值与常数的简单比较,称为筛选条件。这些比较从向下推送到存储层中受益最多。将在 EXPLAIN 输出中报告查询中的筛选条件表达式数。

  • 如果可能,WHERE 子句中的其他类型的表达式也会向下推送到存储层。将在 EXPLAIN 输出中报告查询中的此类表达式数。这些表达式可能是函数调用、LIKE 运算符、CASE 表达式,等等。

  • 目前,并行查询不会向下推送某些函数和运算符。查询中的此类表达式数将在 extra 输出中报告为 EXPLAIN 计数器。查询的其余部分仍然可以使用并行查询。

  • 虽然不会向下推送选择列表中的表达式,但包含此类函数的查询仍然可以从并行查询的中间结果的网络流量减少中受益。例如,在选择列表中调用聚合函数的查询可以从并行查询中受益,即使不向下推送聚合函数。

例如,以下查询执行全表扫描并处理 P_BRAND 列的所有值。不过,它不使用并行查询,因为查询不包含任何 WHERE 子句。

mysql> explain select count(*), p_brand from part group by p_brand; +----+-------------+-------+------+---------------+------+---------+------+----------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+----------+---------------------------------+ | 1 | SIMPLE | part | ALL | NULL | NULL | NULL | NULL | 20427936 | Using temporary; Using filesort | +----+-------------+-------+------+---------------+------+---------+------+----------+---------------------------------+

相反,以下查询包括筛选结果的 WHERE 谓词,因此,可以应用并行查询:

mysql> explain select count(*), p_brand from part where p_name is not null -> and p_mfgr in ('Manufacturer#1', 'Manufacturer#3') and p_retailprice > 1000 -> group by p_brand; +----+...+----------+-------------------------------------------------------------------------------------------------------------+ | id |...| rows | Extra | +----+...+----------+-------------------------------------------------------------------------------------------------------------+ | 1 |...| 20427936 | Using where; Using temporary; Using filesort; Using parallel query (5 columns, 1 filters, 2 exprs; 0 extra) | +----+...+----------+-------------------------------------------------------------------------------------------------------------+

如果优化程序估计查询块的返回行数很少,则不会在该查询块中使用并行查询。以下示例说明了一种主键列上的大于运算符应用于数百万行的情况,这会导致使用并行查询。估计反向小于测试仅应用于几行,而不使用并行查询。

mysql> explain select count(*) from part where p_partkey > 10; +----+...+----------+----------------------------------------------------------------------------+ | id |...| rows | Extra | +----+...+----------+----------------------------------------------------------------------------+ | 1 |...| 20427936 | Using where; Using parallel query (1 columns, 1 filters, 0 exprs; 0 extra) | +----+...+----------+----------------------------------------------------------------------------+ mysql> explain select count(*) from part where p_partkey < 10; +----+...+------+--------------------------+ | id |...| rows | Extra | +----+...+------+--------------------------+ | 1 |...| 9 | Using where; Using index | +----+...+------+--------------------------+

数据定义语言 (DDL)

在 Aurora MySQL 版本 2 中,并行查询仅适用于没有挂起的快速数据定义语言(DDL)操作的表。在 Aurora MySQL 版本 3 中,您可以在表上与即时 DDL 操作同时使用并行查询。

Aurora MySQL 版本 3 中的即时 DDL 取代了 Aurora MySQL 版本 2 中的快速 DDL 功能。有关即时 DDL 的信息,请参阅 即时 DDL(Aurora MySQL 版本 3)

列数据类型

在 Aurora MySQL 版本 3 中,并行查询可以处理包含具有数据类型 TEXTBLOBJSONGEOMETRY 的列的表。它也可以使用最大声明长度超过 768 字节的 VARCHARCHAR 列。如果您的查询引用任何包含此类大型对象类型的列,则检索它们的额外工作确实会增加查询处理的一些开销。在这种情况下,请检查查询是否可以省略对这些列的引用。如果没有,运行基准测试以确认在开启或关闭并行查询的情况下,此类查询是否更快。

在 Aurora MySQL 版本 2 中,并行查询对于大型对象类型有以下限制:

  • TEXTBLOBJSONGEOMETRY 数据类型不支持并行查询。引用这些类型的任何列的查询无法使用并行查询。

  • 可变长度列(VARCHARCHAR 数据类型)与并行查询兼容,最大声明长度最多为 768 字节。如果查询引用的任何列具有使用更长最大长度声明的类型,则无法使用并行查询。对于使用多字节字符集的列,字节限制将字符集中的最大字节数考虑在内。例如,对于字符集 utf8mb4(最大字符长度为 4 字节),VARCHAR(192) 列与并行查询兼容,但 VARCHAR(193) 列不兼容。

分区表

在 Aurora MySQL 版本 3 中,您可以将分区表与并行查询结合使用。由于分区表在内部表示为多个较小的表,因此对非分区表使用并行查询的查询可能不会对相同的分区表使用并行查询。Aurora MySQL 考虑每个分区是否足以符合并行查询优化条件,而不是评估整个表的大小。检查是否Aurora_pq_request_not_chosen_small_table如果分区表上的查询在预期时不使用并行查询,则状态变量将递增。

例如,考虑用 PARTITION BY HASH (column) PARTITIONS 2 分区的一个表和用 PARTITION BY HASH (column) PARTITIONS 10 分区的另一个表。在有两个分区的表中,分区的大小是有十个分区的表的五倍。因此,并行查询更有可能用于对分区较少的表进行查询。在以下示例中,表 PART_BIG_PARTITIONS 有两个分区,PART_SMALL_PARTITIONS 有十个分区。在数据相同的情况下,并行查询更有可能用于大分区较少的表。

mysql> explain select count(*), p_brand from part_big_partitions where p_name is not null -> and p_mfgr in ('Manufacturer#1', 'Manufacturer#3') and p_retailprice > 1000 group by p_brand; +----+-------------+---------------------+------------+-------------------------------------------------------------------------------------------------------------------+ | id | select_type | table | partitions | Extra | +----+-------------+---------------------+------------+-------------------------------------------------------------------------------------------------------------------+ | 1 | SIMPLE | part_big_partitions | p0,p1 | Using where; Using temporary; Using parallel query (4 columns, 1 filters, 1 exprs; 0 extra; 1 group-bys, 1 aggrs) | +----+-------------+---------------------+------------+-------------------------------------------------------------------------------------------------------------------+ mysql> explain select count(*), p_brand from part_small_partitions where p_name is not null -> and p_mfgr in ('Manufacturer#1', 'Manufacturer#3') and p_retailprice > 1000 group by p_brand; +----+-------------+-----------------------+-------------------------------+------------------------------+ | id | select_type | table | partitions | Extra | +----+-------------+-----------------------+-------------------------------+------------------------------+ | 1 | SIMPLE | part_small_partitions | p0,p1,p2,p3,p4,p5,p6,p7,p8,p9 | Using where; Using temporary | +----+-------------+-----------------------+-------------------------------+------------------------------+

聚合函数、GROUP BY 子句和 HAVING 子句

涉及聚合函数的查询通常是并行查询的理想之选,因为它们涉及扫描大型表中的大量行。

在 Aurora MySQL 3 中,并行查询可以优化选择列表和 HAVING 子句中的聚合函数调用。

在 Aurora MySQL 3 之前,选择列表或 HAVING 子句中的聚合函数调用不会向下推送到存储层。不过,并行查询仍然可以使用聚合函数提高此类查询的性能。为此,它先在存储层中从原始数据页面中并行提取列值。然后,它以紧凑元组格式将这些值发回到头节点,而不是作为完整数据页面。与往常一样,查询需要具有至少一个 WHERE 谓词才能激活并行查询。

以下简单示例说明了可以从并行查询中受益的聚合查询种类。它们以紧凑形式将中间结果返回到头节点以及/或者从中间结果中筛选不匹配的行。

mysql> explain select sql_no_cache count(distinct p_brand) from part where p_mfgr = 'Manufacturer#5'; +----+...+----------------------------------------------------------------------------+ | id |...| Extra | +----+...+----------------------------------------------------------------------------+ | 1 |...| Using where; Using parallel query (2 columns, 1 filters, 0 exprs; 0 extra) | +----+...+----------------------------------------------------------------------------+ mysql> explain select sql_no_cache p_mfgr from part where p_retailprice > 1000 group by p_mfgr having count(*) > 100; +----+...+-------------------------------------------------------------------------------------------------------------+ | id |...| Extra | +----+...+-------------------------------------------------------------------------------------------------------------+ | 1 |...| Using where; Using temporary; Using filesort; Using parallel query (3 columns, 0 filters, 1 exprs; 0 extra) | +----+...+-------------------------------------------------------------------------------------------------------------+

WHERE 子句中的函数调用

Aurora 可以将并行查询优化应用于 WHERE 子句中的大多数内置函数调用。并行处理这些函数调用将会从头节点中卸载一些 CPU 负载。通过在最早的查询阶段并行计算谓词函数,有助于 Aurora 最大限度减少在后续阶段传输和处理的数据量。

目前,并行处理不适用于选择列表中的函数调用。头节点计算这些函数,即使在 WHERE 子句中出现相同的函数调用。来自相关列的原始值包含在从存储节点发回到头节点的元组中。头节点执行任何转换(如 UPPERCONCATENATE 等)以生成结果集的最终值。

在以下示例中,并行查询将并行处理对 LOWER 的调用,因为它出现在 WHERE 子句中。并行查询不会影响对 SUBSTRUPPER 的调用,因为它们出现在选择列表中。

mysql> explain select sql_no_cache distinct substr(upper(p_name),1,5) from part -> where lower(p_name) like '%cornflower%' or lower(p_name) like '%goldenrod%'; +----+...+---------------------------------------------------------------------------------------------+ | id |...| Extra | +----+...+---------------------------------------------------------------------------------------------+ | 1 |...| Using where; Using temporary; Using parallel query (2 columns, 0 filters, 1 exprs; 0 extra) | +----+...+---------------------------------------------------------------------------------------------+

相同的注意事项适用于其他表达式,例如,CASE 表达式或 LIKE 运算符。例如,以下示例显示并行查询计算 CASE 子句中的 LIKE 表达式和 WHERE 运算符。

mysql> explain select p_mfgr, p_retailprice from part -> where p_retailprice > case p_mfgr -> when 'Manufacturer#1' then 1000 -> when 'Manufacturer#2' then 1200 -> else 950 -> end -> and p_name like '%vanilla%' -> group by p_retailprice; +----+...+-------------------------------------------------------------------------------------------------------------+ | id |...| Extra | +----+...+-------------------------------------------------------------------------------------------------------------+ | 1 |...| Using where; Using temporary; Using filesort; Using parallel query (4 columns, 0 filters, 2 exprs; 0 extra) | +----+...+-------------------------------------------------------------------------------------------------------------+

LIMIT 子句

目前,并行查询不用于包含 LIMIT 子句的任何查询块。并行查询可能仍用于具有 GROUP by、ORDER BY 或联接的早期查询阶段。

比较运算符

优化程序估计要扫描的行数以计算比较运算符,并根据该估计确定是否使用并行查询。

下面的第一个示例显示,可以在没有并行查询的情况下高效地执行与主键列的相等比较。下面的第二个示例显示,与未编制索引的列进行类似比较需要扫描数百万行,因此,可以从并行查询中受益。

mysql> explain select * from part where p_partkey = 10; +----+...+------+-------+ | id |...| rows | Extra | +----+...+------+-------+ | 1 |...| 1 | NULL | +----+...+------+-------+ mysql> explain select * from part where p_type = 'LARGE BRUSHED BRASS'; +----+...+----------+----------------------------------------------------------------------------+ | id |...| rows | Extra | +----+...+----------+----------------------------------------------------------------------------+ | 1 |...| 20427936 | Using where; Using parallel query (9 columns, 1 filters, 0 exprs; 0 extra) | +----+...+----------+----------------------------------------------------------------------------+

同样的注意事项适用于不等于测试和范围比较,例如小于、大于或等于或 BETWEEN。优化程序估计要扫描的行数,并根据 I/O 总量确定是否值得运行并行查询。

联接

大型表的联接查询通常涉及数据密集型操作,这些操作将从并行查询优化中受益。目前,不会并行处理多个表之间的列值比较(即,联接谓词本身)。不过,并行查询可以向下推送其他联接阶段的一些内部处理,例如,在哈希联接期间构建 Bloom 筛选条件。即使没有 WHERE 子句,并行查询也可以应用于联接查询。因此,对于需要使用 WHERE 子句以使用并行查询的规则,联接查询是一种例外情况。

将计算联接处理的每个阶段以检查它是否符合并行查询条件。如果多个阶段可以使用并行查询,将按顺序执行这些阶段。因此,每个联接查询在并发限制中计为单个并行查询会话。

例如,如果联接查询包含 WHERE 谓词以筛选联接的某个表中的行,该筛选选项可以使用并行查询。作为另一个示例,假设联接查询使用哈希联接机制,例如,将大表与小表联接在一起。在这种情况下,用于生成 Bloom 筛选条件数据结构的表扫描或许可以使用并行查询。

注意

并行查询通常用于受益于哈希联接优化的各种资源密集型查询。开启哈希联接优化的方法取决于 Aurora MySQL 版本。有关各版本的详细信息,请参阅 为并行查询集群开启哈希联接。有关如何有效使用哈希联接的信息,请参阅 使用哈希联接优化大型 Aurora MySQL 联接查询

mysql> explain select count(*) from orders join customer where o_custkey = c_custkey; +----+...+----------+-------+---------------+-------------+...+-----------+-----------------------------------------------------------------------------------------------------------------+ | id |...| table | type | possible_keys | key |...| rows | Extra | +----+...+----------+-------+---------------+-------------+...+-----------+-----------------------------------------------------------------------------------------------------------------+ | 1 |...| customer | index | PRIMARY | c_nationkey |...| 15051972 | Using index | | 1 |...| orders | ALL | o_custkey | NULL |...| 154545408 | Using join buffer (Hash Join Outer table orders); Using parallel query (1 columns, 0 filters, 1 exprs; 0 extra) | +----+...+----------+-------+---------------+-------------+...+-----------+-----------------------------------------------------------------------------------------------------------------+

对于使用嵌套循环机制的联接查询,最外层的嵌套循环块可能会使用并行查询。是否使用并行查询取决于与往常相同的因素,例如,是否在 WHERE 子句中存在其他筛选条件。

mysql> -- Nested loop join with extra filter conditions can use parallel query. mysql> explain select count(*) from part, partsupp where p_partkey != ps_partkey and p_name is not null and ps_availqty > 0; +----+-------------+----------+...+----------+----------------------------------------------------------------------------+ | id | select_type | table |...| rows | Extra | +----+-------------+----------+...+----------+----------------------------------------------------------------------------+ | 1 | SIMPLE | part |...| 20427936 | Using where; Using parallel query (2 columns, 1 filters, 0 exprs; 0 extra) | | 1 | SIMPLE | partsupp |...| 78164450 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+----------+...+----------+----------------------------------------------------------------------------+

子查询

外部查询块和内部子查询块可能分别使用并行查询,也可能不使用。它们是否这样做取决于针对每个块的表、 WHERE 子句等的常用特征。例如,以下查询在子查询块中使用并行查询,但在外部块中不使用并行查询。

mysql> explain select count(*) from part where --> p_partkey < (select max(p_partkey) from part where p_name like '%vanilla%'); +----+-------------+...+----------+----------------------------------------------------------------------------+ | id | select_type |...| rows | Extra | +----+-------------+...+----------+----------------------------------------------------------------------------+ | 1 | PRIMARY |...| NULL | Impossible WHERE noticed after reading const tables | | 2 | SUBQUERY |...| 20427936 | Using where; Using parallel query (2 columns, 0 filters, 1 exprs; 0 extra) | +----+-------------+...+----------+----------------------------------------------------------------------------+

目前,关联子查询无法使用并行查询优化。

联合

对于 UNION 的每个部分,根据表和 WHERE 子句等的通常特性,UNION 查询中的每个查询块可能会使用或不使用并行查询。

mysql> explain select p_partkey from part where p_name like '%choco_ate%' -> union select p_partkey from part where p_name like '%vanil_a%'; +----+----------------+...+----------+----------------------------------------------------------------------------+ | id | select_type |...| rows | Extra | +----+----------------+...+----------+----------------------------------------------------------------------------+ | 1 | PRIMARY |...| 20427936 | Using where; Using parallel query (2 columns, 0 filters, 1 exprs; 0 extra) | | 2 | UNION |...| 20427936 | Using where; Using parallel query (2 columns, 0 filters, 1 exprs; 0 extra) | | NULL | UNION RESULT | <union1,2> |...| NULL | Using temporary | +----+--------------+...+----------+----------------------------------------------------------------------------+
注意

查询中的每个 UNION 子句是按顺序运行的。即使查询包含的多个阶段均使用并行查询,它在任何时间也仅运行单个并行查询。因此,甚至是复杂的多阶段查询也仅在并发并行查询限制中计为 1 个。

视图

优化程序将视图作为使用基础表的较长查询以重写任何查询。因此,无论表引用是视图还是实际表,并行查询的工作方式都是相同的。有关在查询中是否使用并行查询以及向下推送哪些部分的所有相同注意事项适用于最终重写的查询。

例如,以下查询计划显示通常不使用并行查询的视图定义。在使用额外的 WHERE 子句查询视图时,Aurora MySQL 使用并行查询。

mysql> create view part_view as select * from part; mysql> explain select count(*) from part_view where p_partkey is not null; +----+...+----------+----------------------------------------------------------------------------+ | id |...| rows | Extra | +----+...+----------+----------------------------------------------------------------------------+ | 1 |...| 20427936 | Using where; Using parallel query (1 columns, 0 filters, 0 exprs; 1 extra) | +----+...+----------+----------------------------------------------------------------------------+

数据操作语言 (DML) 语句

如果 INSERT 部分满足并行查询的其他条件,则 SELECT 语句可以在 SELECT 处理阶段中使用并行查询。

mysql> create table part_subset like part; mysql> explain insert into part_subset select * from part where p_mfgr = 'Manufacturer#1'; +----+...+----------+----------------------------------------------------------------------------+ | id |...| rows | Extra | +----+...+----------+----------------------------------------------------------------------------+ | 1 |...| 20427936 | Using where; Using parallel query (9 columns, 1 filters, 0 exprs; 0 extra) | +----+...+----------+----------------------------------------------------------------------------+
注意

通常,在执行 INSERT 语句后,新插入的行的数据将位于缓冲池中。因此,在插入大量行后,表可能不符合并行查询条件。以后,在正常运行期间从缓冲池中移出数据后,针对表的查询可能会再次开始使用并行查询。

CREATE TABLE AS SELECT 语句不使用并行查询,即使该语句的 SELECT 部分符合并行查询条件。该语句的 DDL 特性导致它与并行查询处理不兼容。相反,在 INSERT ... SELECT 语句中,SELECT 部分可以使用并行查询。

DELETEUPDATE 语句中从不使用并行查询,而无论表的大小和 WHERE 子句中的谓词如何。

mysql> explain delete from part where p_name is not null; +----+-------------+...+----------+-------------+ | id | select_type |...| rows | Extra | +----+-------------+...+----------+-------------+ | 1 | SIMPLE |...| 20427936 | Using where | +----+-------------+...+----------+-------------+

事务和锁定

您可以在 Aurora 主实例上使用所有隔离级别。

在 Aurora 读取器数据库实例上,并行查询适用于 REPEATABLE READ 隔离级别下执行的语句。Aurora MySQL 版本 2.09 或更高版本也可以在读取器数据库实例上使用 READ COMMITTED 隔离级别。REPEATABLE READ 是 Aurora 读取器数据库实例的原定设置隔离级别。要在读取器数据库实例上使用 READ COMMITTED 隔离级别,需要在会话级别设置 aurora_read_replica_read_committed 配置选项。读取器实例的 READ COMMITTED 隔离级别符合 SQL 标准行为。但是,与查询在写入器实例上使用 READ COMMITTED 隔离级别时相比,对读取器实例的隔离没有那么严格。

有关 Aurora 隔离级别的更多信息,特别是写入器实例与读取器实例之间的 READ COMMITTED 区别,请参阅 Aurora MySQL 隔离级别

在较大的事务完成后,表统计数据可能会过时。这种过时的统计数据可能要求使用 ANALYZE TABLE 语句,然后 Aurora 才能准确估计行数。大型 DML 语句可能还会将大部分的表数据放入缓冲池中。将该数据放入缓冲池可能会导致不常为该表选择并行查询,直到将数据从池中移出。

如果您的会话位于长时间运行的事务中(默认为 10 分钟),则该会话中的其他查询不会使用并行查询。在单个长时间运行的查询期间,也可能会发生超时。如果查询在并行查询处理开始之前运行的时间超过最大间隔(当前为 10 分钟),则可能会发生这种超时。

您可以在执行临时(一次)查询的 autocommit=1 会话中设置 mysql,以降低意外启动长时间运行的事务的可能性。甚至针对表的 SELECT 语句也可以创建读取视图以开始运行事务。读取视图 是一个用于后续查询的一致数据集,将在提交事务之前保留该数据集。在 Aurora 中使用 JDBC 或 ODBC 应用程序时,也要注意该限制,因为此类应用程序可能会在禁用 autocommit 设置的情况下运行。

以下示例显示对表运行查询如何创建一个读取视图以隐式开始运行事务(在禁用了 autocommit 设置的情况下)。稍后运行的查询仍然可以使用并行查询。不过,在暂停几分钟后,查询不再符合并行查询条件。如果将事务以 COMMITROLLBACK 结尾,将恢复并行查询条件。

mysql> set autocommit=0; mysql> explain select sql_no_cache count(*) from part where p_retailprice > 10.0; +----+...+---------+----------------------------------------------------------------------------+ | id |...| rows | Extra | +----+...+---------+----------------------------------------------------------------------------+ | 1 |...| 2976129 | Using where; Using parallel query (1 columns, 1 filters, 0 exprs; 0 extra) | +----+...+---------+----------------------------------------------------------------------------+ mysql> select sleep(720); explain select sql_no_cache count(*) from part where p_retailprice > 10.0; +------------+ | sleep(720) | +------------+ | 0 | +------------+ 1 row in set (12 min 0.00 sec) +----+...+---------+-------------+ | id |...| rows | Extra | +----+...+---------+-------------+ | 1 |...| 2976129 | Using where | +----+...+---------+-------------+ mysql> commit; mysql> explain select sql_no_cache count(*) from part where p_retailprice > 10.0; +----+...+---------+----------------------------------------------------------------------------+ | id |...| rows | Extra | +----+...+---------+----------------------------------------------------------------------------+ | 1 |...| 2976129 | Using where; Using parallel query (1 columns, 1 filters, 0 exprs; 0 extra) | +----+...+---------+----------------------------------------------------------------------------+

要查看查询由于位于长时间运行的事务中而不符合并行查询条件的次数,请检查 Aurora_pq_request_not_chosen_long_trx 状态变量。

mysql> show global status like '%pq%trx%'; +---------------------------------------+-------+ | Variable_name | Value | +---------------------------------------+-------+ | Aurora_pq_request_not_chosen_long_trx | 4 | +-------------------------------+-------+

获取锁定的任何 SELECT 语句(如 SELECT FOR UPDATESELECT LOCK IN SHARE MODE 语法)无法使用并行查询。

并行查询可以用于 LOCK TABLES 语句锁定的表。

mysql> explain select o_orderpriority, o_shippriority from orders where o_clerk = 'Clerk#000095055'; +----+...+-----------+----------------------------------------------------------------------------+ | id |...| rows | Extra | +----+...+-----------+----------------------------------------------------------------------------+ | 1 |...| 154545408 | Using where; Using parallel query (3 columns, 1 filters, 0 exprs; 0 extra) | +----+...+-----------+----------------------------------------------------------------------------+ mysql> explain select o_orderpriority, o_shippriority from orders where o_clerk = 'Clerk#000095055' for update; +----+...+-----------+-------------+ | id |...| rows | Extra | +----+...+-----------+-------------+ | 1 |...| 154545408 | Using where | +----+...+-----------+-------------+

B 树索引

根据每个列的数据特性,ANALYZE TABLE 语句收集的统计数据可以帮助优化程序确定何时使用并行查询或索引查找。在执行对表中的数据进行重大更改的 DML 操作后,请运行 ANALYZE TABLE 以将统计数据保持最新状态。

如果索引查找可以在没有数据密集型扫描的情况下高效地执行查询,则 Aurora 可能会使用索引查找。这样做可以避免并行查询处理的开销。可以在任何 Aurora 数据库集群上同时运行的并行查询数也存在并发限制。确保使用最佳实践为表编制索引,以便最频繁和最高并发性的查询使用索引查找。

全文搜索 (FTS) 索引

目前,并行查询不用于包含全文搜索索引的表,而无论查询是引用此类索引列,还是使用 MATCH 运算符。

虚拟列

目前,并行查询不适用于包含虚拟列的表,无论查询是否引用任何虚拟列。

内置缓存机制

Aurora 包括内置缓存机制,即,缓冲池和查询缓存。Aurora 优化程序在这些缓存机制和并行查询之间进行选择,具体取决于哪一个对特定查询最有效。

在并行查询筛选行并转换和提取列值时,数据将作为元组而不是数据页面传回到头节点。因此,运行并行查询不会将任何页面添加到缓冲池中,也不会移出已位于缓冲池中的页面。

Aurora 会检查位于缓冲池中的表数据页数,以及该数字所代表的表数据比例。Aurora 会通过该信息确定使用并行查询是否更加高效,并且决定是否需要绕过缓冲池中的数据。或者,Aurora 可能使用非并行查询处理路径,这会使用缓冲池中缓存的数据。缓存哪些页面以及数据密集型查询如何影响缓存和移出取决于与缓冲池相关的配置设置。因此,很难预测任何特定查询是否使用并行查询,因为这取决于缓冲池中不断变化的数据。

此外,Aurora 对并行查询施加并发限制。由于并非每个查询都使用并行查询,多个查询同时访问的表通常将大部分数据放在缓冲池中。因此,Aurora 通常不会选择这些表以运行并行查询。

在同一个表上运行一系列非并行查询时,由于数据没有位于缓冲池中,第一个查询的速度可能很慢。由于缓冲池现已“预热”,第二次和后续查询要快得多。并行查询通常从针对表的第一个查询开始就具有一致的性能。在进行性能测试时,将使用冷缓冲池和热缓冲池对非并行查询进行基准测试。在某些情况下,使用热缓冲池的结果可能与并行查询时间接近。在这些情况下,请考虑诸如针对该表进行查询的频率等因素。还要考虑是否值得将该表的数据保留在缓冲池中。

在提交相同的查询以及未更改基础表数据时,查询缓存可以避免重新运行查询。并行查询功能优化的查询可以放入查询缓存中,实际上可以在再次运行时立即运行它们。

注意

在进行性能比较时,查询缓存可能会生成虚假的低计时数。因此,在与基准测试类似的情况下,您可以使用 sql_no_cache 提示。该提示可以防止从查询缓存中提供结果,即使以前运行了相同的查询。该提示直接位于查询中的 SELECT 语句后面。本主题中的很多并行查询示例包括该提示,以使开启和关闭并行查询的查询版本具有类似的查询时间。

在生产环境中使用并行查询时,请确保从源中删除该提示。

优化程序提示

控制优化程序的另一种方法是使用优化程序提示,可以在单个语句中指定优化程序提示。例如,您可以对语句中的一个表开启优化,然后对另一个表关闭优化。有关这些提示的更多信息,请参阅《MySQL 参考手册》中的优化程序提示

您可以将 SQL 提示与 Aurora MySQL 查询结合使用来微调性能。您还可以使用提示来防止重要查询的执行计划由于不可预知的条件而发生变化。

我们扩展了 SQL 提示功能,以帮助您控制查询计划的优化程序选择。这些提示适用于使用并行查询优化的查询。有关更多信息,请参阅 Aurora MySQL 提示

MyISAM 临时表

并行查询优化仅适用于 InnoDB 表。由于 Aurora MySQL 在后台的临时表中使用 MyISAM,因此,涉及临时表的内部查询阶段从不使用并行查询。这些查询阶段由 Using temporary 输出中的 EXPLAIN 指示。