Amazon Redshift
数据库开发人员指南 (API Version 2012-12-01)
AWS 服务或AWS文档中描述的功能,可能因地区/位置而异。点 击 Getting Started with Amazon AWS to see specific differences applicable to the China (Beijing) Region.

查询计划

您可以借助查询计划获取有关执行查询所需的各个操作的信息。在处理查询计划前,建议您先了解 Amazon Redshift 如何处理查询和如何创建查询计划。有关更多信息,请参阅 查询计划和执行工作流程

要创建查询计划,请运行后跟实际查询文本的 EXPLAIN 命令。查询计划提供以下信息:

  • 执行引擎将执行的操作,自下而上地阅读结果。

  • 每个操作执行的步骤的类型。

  • 每个操作中使用的表和列。

  • 每个操作中处理的数据量,以行数和数据宽度(字节)为单位。

  • 操作的相对成本。成本 是比较计划内步骤相对执行时间的一种度量。成本不提供有关实际执行时间或内存占用的任何准确信息,也不提供执行计划间有意义的比较。它指示查询中的哪些操作占用的资源最多。

EXPLAIN 命令不实际运行查询。它只显示当查询在当前操作条件下运行时 Amazon Redshift 将执行的计划。如果您更改表的 schema 或数据后再次运行 ANALYZE 以更新统计元数据,则查询计划可能会不同。

EXPLAIN 输出的查询计划是查询执行过程的简要视图。它不描述并行查询处理的详细信息。要查看详细信息,您需要运行查询本身,然后从 SVL_QUERY_SUMMARY 或 SVL_QUERY_REPORT 视图获取查询摘要信息。有关使用这些视图的更多信息,请参阅分析查询摘要

下面的示例显示对 EVENT 表执行简单 GROUP BY 查询的 EXPLAIN 输出:

Copy
explain select eventname, count(*) from event group by eventname; QUERY PLAN ------------------------------------------------------------------- XN HashAggregate (cost=131.97..133.41 rows=576 width=17) -> XN Seq Scan on event (cost=0.00..87.98 rows=8798 width=17)

EXPLAIN 返回每个操作的以下指标:

成本

一个相对值,要比较计划中的操作,它很有用。成本由两个圆点分隔的两个数值组成,例如 cost=131.97..133.41。第一个值(本例中的 131.97)提供返回该操作第一行的相对成本。第二个值(本例中的 133.41)提供完成操作的相对成本。查询计划中的成本是由下而上累积的,因此,本示例中的 HashAggregate 成本 (131.97..133.41) 包含其下方的 Seq Scan 的成本 (0.00..87.98)。

行数

预计返回的行数。在本示例中,预计扫描将返回 8798 行。预计 HashAggregate 运算符自身将返回 576 行(从结果集中丢弃重复的事件名称后)。

注意

行数估算基于 ANALYZE 命令生成的可用统计数据。如果最近未运行过 ANALYZE,则估算的可靠性会降低。

宽度

平均行的预计宽度(以字节为单位)。在本示例中,平均行预计为 17 个字节宽。

EXPLAIN 运算符

本节简要介绍 EXPLAIN 输出中最常出现的运算符。有关运算符的完整列表,请参阅“SQL 命令”一节中的 EXPLAIN

顺序扫描运算符

顺序扫描运算符 (Seq Scan) 表示表扫描。Seq Scan 从头到尾按顺序扫描表中的每列,并评估每行的查询约束(在 WHERE 子句中)。

联接运算符

Amazon Redshift 根据要联接的表的物理设计、联接所需的数据的位置以及查询本身的特定要求来选择联接运算符。

  • Nested Loop

    嵌套循环是优化程度最差的联接,主要用于交叉联接(笛卡尔积)和一些不等式联接。

  • 哈希联接和哈希

    哈希联接和哈希通常较嵌套循环联接快,用于内部联接和左/右外部联接。这些运算符在对联接列不全是分配键 排序键的表进行联接时使用。哈希运算符为联接运算的内部表创建哈希表;哈希联接运算符读取外部表,对联接列进行哈希处理,然后在内部哈希表查找匹配项。

  • 合并联接

    合并联接通常是最快的联接,用于内部联接和外部联接。合并联接不用于完全联接。在联接其联接列同时为分配键 排序键的表时,以及当 20% 以下的联接表未排序时,将使用此运算符。它按顺序读取两个排序表并查找匹配的行。要查看未排序行的百分比,请查询 SVV_TABLE_INFO 系统表。

聚合运算符

查询计划在涉及聚合函数和 GROUP BY 操作的查询中使用以下运算符。

  • Aggregate

    标量聚合函数的运算符,如 AVG 和 SUM。

  • HashAggregate

    用于未排序分组聚合函数的运算符。

  • GroupAggregate

    用于排序分组聚合函数的运算符。

排序运算符

当查询必须排序或合并结果集时,查询计划使用以下运算符。

  • Sort

    评估 ORDER BY 子句及其他排序操作,如 UNION 查询和联接、SELECT DISTINCT 查询及窗口函数需要的排序。

  • Merge

    根据从并行操作得到的中间排序结果生成最终排序结果。

UNION、INTERSECT 和 EXCEPT 运算符

对于涉及 UNION、INTERSECT 和 EXCEPT 集合运算的查询,查询计划使用以下运算符。

  • Subquery

    用于运行 UNION 查询。

  • Hash Intersect Distinct 和 Hash Intersect All

    用于运行 INTERSECT 和 INTERSECT ALL 查询。

  • SetOp Except

    用于运行 EXCEPT(或 MINUS)查询。

其他运算符

以下运算符也常在常规查询的 EXPLAIN 输出中出现。

  • Unique

    消除重复的 SELECT DISTINCT 查询和 UNION 查询。

  • Limit

    处理 LIMIT 子句。

  • Window

    运行窗口函数。

  • Result

    运行不涉及任何表访问的标量函数。

  • Subplan

    用于特定的子查询。

  • Network

    将中间结果发送到领导节点进一步处理。

  • Materialize

    保存行以用于嵌套循环联接和某些合并联接的输入。

EXPLAIN 中的联接

查询优化程序使用不同的联接类型检索表数据,具体视查询和基础表的结构而定。EXPLAIN 输出引用联接类型、所用的表及表数据在集群上的分配方式,以描述查询的处理过程。

联接类型示例

以下示例介绍查询优化程序可以使用的不同联接类型。查询计划中使用的联接类型取决于相关表的物理设计。

示例:对两个表进行哈希联接

下面的查询在 CATID 列上联接 EVENT 和 CATEGORY。CATID 是 CATEGORY(但不是 EVENT)的分配键和排序键。哈希联接以 EVENT 作为外部表、CATEGORY 作为内部表执行。因为 CATEGORY 是较小的表,所以,计划程序在查询期间使用 DS_BCAST_INNER 将其副本广播到计算节点。在本示例中,联接成本占计划累积成本中的绝大部分。

Copy
explain select * from category, event where category.catid=event.catid; QUERY PLAN ------------------------------------------------------------------------- XN Hash Join DS_BCAST_INNER (cost=0.14..6600286.07 rows=8798 width=84) Hash Cond: ("outer".catid = "inner".catid) -> XN Seq Scan on event (cost=0.00..87.98 rows=8798 width=35) -> XN Hash (cost=0.11..0.11 rows=11 width=49) -> XN Seq Scan on category (cost=0.00..0.11 rows=11 width=49)

注意

EXPLAIN 输出中运算符的缩进对齐有时表示这些操作不互相依赖,可以并行执行。但在前面的示例中,尽管对 EVENT 表的扫描和哈希操作是对齐的,但 EVENT 扫描必须等待哈希操作彻底完成。

示例:合并联接两个表

下面的查询还使用 SELECT *,但它在 LISTID 列上联接 SALES 和 LISTING,其中,LISTID 已设置为两个表的分配键及排序键。选择了合并联接,该联接不要求重新分配数据 (DS_DIST_NONE)。

Copy
explain select * from sales, listing where sales.listid = listing.listid; QUERY PLAN ----------------------------------------------------------------------------- XN Merge Join DS_DIST_NONE (cost=0.00..6285.93 rows=172456 width=97) Merge Cond: ("outer".listid = "inner".listid) -> XN Seq Scan on listing (cost=0.00..1924.97 rows=192497 width=44) -> XN Seq Scan on sales (cost=0.00..1724.56 rows=172456 width=53)

下面的示例演示同一查询中的不同联接类型。如前例所示,SALES 和 LISTING 合并联接,但第三个表 EVENT 必须与该合并联接的结果进行哈希联接。再次说明,哈希联接会产生广播费用。

Copy
explain select * from sales, listing, event where sales.listid = listing.listid and sales.eventid = event.eventid; QUERY PLAN ---------------------------------------------------------------------------- XN Hash Join DS_BCAST_INNER (cost=109.98..3871130276.17 rows=172456 width=132) Hash Cond: ("outer".eventid = "inner".eventid) -> XN Merge Join DS_DIST_NONE (cost=0.00..6285.93 rows=172456 width=97) Merge Cond: ("outer".listid = "inner".listid) -> XN Seq Scan on listing (cost=0.00..1924.97 rows=192497 width=44) -> XN Seq Scan on sales (cost=0.00..1724.56 rows=172456 width=53) -> XN Hash (cost=87.98..87.98 rows=8798 width=35) -> XN Seq Scan on event (cost=0.00..87.98 rows=8798 width=35)

示例:联接、聚合和排序

下面的查询执行 SALES 和 EVENT 表的哈希联接,后跟聚合和排序操作,以便为执行 SUM 函数及 ORDER BY 子句做好准备。初始 Sort 运算符在计算节点上并行运行。然后,Network 运算符将结果发送到领导节点,在领导节点上,Merge 运算符产生最终排序结果。

Copy
explain select eventname, sum(pricepaid) from sales, event where sales.eventid=event.eventid group by eventname order by 2 desc; QUERY PLAN --------------------------------------------------------------------------------- XN Merge (cost=1002815366604.92..1002815366606.36 rows=576 width=27) Merge Key: sum(sales.pricepaid) -> XN Network (cost=1002815366604.92..1002815366606.36 rows=576 width=27) Send to leader -> XN Sort (cost=1002815366604.92..1002815366606.36 rows=576 width=27) Sort Key: sum(sales.pricepaid) -> XN HashAggregate (cost=2815366577.07..2815366578.51 rows=576 width=27) -> XN Hash Join DS_BCAST_INNER (cost=109.98..2815365714.80 rows=172456 width=27) Hash Cond: ("outer".eventid = "inner".eventid) -> XN Seq Scan on sales (cost=0.00..1724.56 rows=172456 width=14) -> XN Hash (cost=87.98..87.98 rows=8798 width=21) -> XN Seq Scan on event (cost=0.00..87.98 rows=8798 width=21)

数据重新分配

联接的 EXPLAIN 输出还指定在集群上移动数据以便进行联接的方法。数据移动可以采用广播方法或重新分配。在广播方法中,联接一侧的数据值从每个计算节点复制到所有其他计算节点,最终使每个计算节点都拥有该数据的完整副本。在重新分配中,参与数据值从其当前切片发送到新的切片(可能在不同的节点上)。如果分配键是联接列之一,则数据通常会重新分配,以匹配参与联接的其他表的分配键。如果两个表都没有基于任一联接列的分配键,则两个表都分配到每个节点,或内部表广播到每个节点。

EXPLAIN 输出还引用内部表和外部表。内部表首先扫描并显示在查询计划中更靠近底部的位置。内部表是用于探测匹配的表。它通常保留在内存中,一般是用于执行哈希操作的源表,只要可能,会是要联接的两个表中较小的一个。外部表是用于匹配内部表的行的源表。它通常从磁盘读取。查询优化程序根据最近一次运行 ANALYZE 命令得到的数据库统计数据选择内部表和外部表。查询的 FROM 子句中的表顺序并不区分内部表和外部表。

您可以通过查询计划中的以下属性了解数据的移动方式(以便执行查询):

  • DS_BCAST_INNER

    将整个内部表的副本广播到所有计算节点。

  • DS_DIST_ALL_NONE

    无需重新分配,因为内部表已使用 DISTSTYLE ALL 分配到所有节点。

  • DS_DIST_NONE

    无需重新分配表。可以进行并置联接,因为相应切片的联接无需在节点间移动数据。

  • DS_DIST_INNER

    内部表重新分配。

  • DS_DIST_OUTER

    外部表重新分配。

  • DS_DIST_ALL_INNER

    整个内部表重新分配到单个切片,因为外部表使用 DISTSTYLE ALL。

  • DS_DIST_BOTH

    两个表都重新分配。