

 从补丁 198 开始，Amazon Redshift 将不再支持创建新的 Python UDF。现有的 Python UDF 将继续正常运行至 2026 年 6 月 30 日。有关更多信息，请参阅[博客文章](https://aws.amazon.com/blogs/big-data/amazon-redshift-python-user-defined-functions-will-reach-end-of-support-after-june-30-2026/)。

# 查询性能优化
<a name="c-optimizing-query-performance"></a>

Amazon Redshift 使用基于结构化查询语言 (SQL) 的查询与系统中的数据和对象进行交互。数据操作语言 (DML) 是您用于查看、添加、更改和删除数据的 SQL 子集。数据定义语言 (DDL) 是您用于添加、更改和删除数据库对象（如表和视图）的 SQL 子集。

系统设置完毕后，您通常使用最多的是 DML，特别是用于检索和查看数据的 [SELECT](r_SELECT_synopsis.md) 命令。要在 Amazon Redshift 中编写高效的数据检索查询，请熟悉 SELECT 并应用[设计表的 Amazon Redshift 最佳实践](c_designing-tables-best-practices.md)中概述的提示，以尽量提高查询效率。

要了解 Amazon Redshift 如何处理查询，请参阅[查询处理](c-query-processing.md)和[查询分析和改进](c-query-tuning.md)两节。然后，您可以将此信息与诊断工具结合应用，以确定并消除查询性能方面的问题。

要确定和解决一些在使用 Amazon Redshift 查询时可能遇到的最常见问题和最严重问题，请参阅[查询故障排除](queries-troubleshooting.md)一节。

**Topics**
+ [查询处理](c-query-processing.md)
+ [查询分析和改进](c-query-tuning.md)
+ [查询故障排除](queries-troubleshooting.md)

# 查询处理
<a name="c-query-processing"></a>

Amazon Redshift 通过分析程序和优化程序路由提交的 SQL 查询，以制订查询计划。然后，执行引擎将查询计划转换为代码并将代码发送到计算节点执行。

**Topics**
+ [查询计划和执行工作流程](c-query-planning.md)
+ [创建和解释查询计划](c-the-query-plan.md)
+ [检查查询计划步骤](reviewing-query-plan-steps.md)
+ [影响查询性能的因素](c-query-performance.md)

# 查询计划和执行工作流程
<a name="c-query-planning"></a>

下图概要介绍查询计划和执行工作流程。

![\[领导节点的查询计划和执行工作流。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/07-QueryPlanning.png)


查询计划和执行工作流程遵循以下步骤：

1. 领导节点接收查询并解析 SQL。

1. 分析程序生成初步查询树，后者是原始查询的逻辑表示。然后，Amazon Redshift 将该查询树输入到查询优化程序中。

1. 优化程序评估并在必要时重写查询以最大限度地提高查询的效率。此过程有时会导致创建多个相关查询来替换单个查询。

1. 优化程序会生成一个查询计划以最佳性能进行执行（如果上一步导致多个查询，则生成多个查询计划）。查询计划指定执行选项，如联接类型、联接顺序、聚合选项和数据分配要求。

   您可以使用 [EXPLAIN](r_EXPLAIN.md) 命令查看查询计划。查询计划是分析和优化复杂查询的基本工具。有关更多信息，请参阅 [创建和解释查询计划](c-the-query-plan.md)。

1. 执行引擎将查询计划转换为*步骤*、*分段*和*流*：  
**步骤**  
每个步骤都是查询执行过程中需要的单独操作。可以组合步骤以允许计算节点执行查询、联接或其他数据库操作。  
**Segment**  
可以通过单个进程完成的几个步骤的组合，也是由计算节点切片执行的最小编译单元。*切片*是 Amazon Redshift 中并行处理的单元。流中的分段并行运行。  
**流**  
要分配到可用计算节点切片上的分段的集合。

   执行引擎基于步骤、段和流生成编译后的代码。编译的代码运行速度比解释的代码快，并且使用的计算容量更少。然后，将此编译的代码广播到计算节点。
**注意**  
在对查询进行基准测试时，您应始终比较查询第二次执行的时间，因为第一次执行时间包括编译代码的开销。有关更多信息，请参阅 [影响查询性能的因素](c-query-performance.md)。

1. 计算节点切片并行运行查询分段。在该流程中，Amazon Redshift 利用优化的网络通信、内存和磁盘管理，将中间结果从一个查询计划步骤传递到下一个。这还有助于加快查询执行速度。

步骤 5 和 6 针对每个流执行一次。引擎为一个流创建可执行分段并将其发送到计算节点。当该流的分段完成时，引擎会生成下一个流的分段。通过这种方式，引擎可以分析先前流中发生的情况（例如，操作是否基于磁盘），以影响下一个流中分段的生成。

计算节点完成后，它们会将查询结果返回到领导节点以进行最终处理。领导节点将数据合并到单个结果集中，并解决任何需要的排序或聚合。然后，领导节点将结果返回至客户端。

**注意**  
如有必要，计算节点可能会在查询执行期间将某些数据返回到领导节点。例如，如果您有一个包含 LIMIT 子句的子查询，则在数据在集群间重新分配以进一步处理前会在领导节点上应用该限制。

# 创建和解释查询计划
<a name="c-the-query-plan"></a>

您可以借助查询计划获取有关运行查询所需的各个操作的信息。在处理查询计划前，建议您先了解 Amazon Redshift 如何处理查询和如何创建查询计划。有关更多信息，请参阅 [查询计划和执行工作流程](c-query-planning.md)。

要创建查询计划，请运行 [EXPLAIN](r_EXPLAIN.md) 命令，后跟实际查询文本。查询计划提供以下信息：
+ 执行引擎将执行的操作，自下而上地阅读结果。
+ 每个操作执行的步骤的类型。
+ 每个操作中使用的表和列。
+ 每个操作中处理的数据量（以字节为单位），以行数和数据宽度计。
+ 操作的相对成本。*成本*是比较计划内的步骤的相对执行时间的度量。成本不提供有关实际执行时间或内存消耗的任何精确信息，也没有提供执行计划之间的有意义的比较。它可以指示查询中的哪些操作消耗最多的资源。

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

EXPLANE 的查询计划输出是查询执行的简化高级视图。它不描述并行查询处理的详细信息。要查看详细信息，请运行查询本身，然后从 SVL\$1QUERY\$1SUMMARY 或 SVL\$1QUERY\$1REPORT 视图获取查询摘要信息。有关使用这些视图的更多信息，请参阅[分析查询摘要](c-analyzing-the-query-summary.md)。

以下示例显示 EVENT 表上的简单 GROUP BY 查询的 EXPLAIN 输出：

```
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) 包括其下面的序列扫描的成本 (0.00..87.98)。

**行数**  
要返回的估计行数。在此示例中，扫描预计将返回 8798 行。HashAggregate 运算符本身应返回 576 行（在从结果集中丢弃重复的事件名称之后）。  
行数估算基于 ANALYZE 命令生成的可用统计数据。如果最近未运行过 ANALYZE，则估算的可靠性会降低。

**宽度**  
平均行的估计宽度（以字节为单位）。在此示例中，平均行的宽度应为 17 个字节。

## EXPLAIN 运算符
<a name="EXPLAIN-operators"></a>

本节简要介绍了在 EXPLAIN 输出中最常见的运算符。有关运算符的完整列表，请参阅 SQL 命令部分中的[EXPLAIN](r_EXPLAIN.md)。

### 顺序扫描运算符
<a name="scan-operator"></a>

顺序扫描运算符 (Seq Scan) 指示表扫描。Seq Scan 扫描从开始到结束按顺序扫描表中的每一列，并计算每一行的查询约束（在 WHERE 子句中）。

### 联接运算符
<a name="join-operators"></a>

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

  最优化程度最差的联接，即嵌套循环，主要用于交叉联接（笛卡尔积）和一些不等式联接。
+ **哈希联接和哈希**

  哈希联接和哈希的运行速度通常比签到循环快，可用于内部联接以及左和右外部联接。这些运算符在联接列不是分配键*和*排序键的情况下用于联接表。哈希运算符为联接中的内部表创建哈希表；哈希联接运算符读取外部表，对联接列进行哈希处理，然后在内部哈希表中查找匹配项。
+ **合并联接**

  合并联接通常是最快的连接，用于内联接和外联接。合并联接不用于完全联接。此运算符在联接列都是分配键*和*排序键的情况下，以及未排序的联接表少于 20% 时用于联接表。它按顺序读取两个排序表并查找匹配的行。要查看未排序行的百分比，请查询 [SVV\$1TABLE\$1INFO](r_SVV_TABLE_INFO.md) 系统表。
+ **空间联接**

  通常是基于空间数据邻近度的快速联接，用于 `GEOMETRY` 和 `GEOGRAPHY` 数据类型。

### 聚合运算符
<a name="aggregate-operators"></a>

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

  标量聚合函数（如 AVG 和 SUM）的运算符。
+ **HashAggregate**

  未排序分组聚合函数的运算符。
+ **GroupAggregate**

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

### 排序运算符
<a name="sort-operators"></a>

当查询必须对结果集进行排序或合并时，查询计划使用以下运算符。
+ **排序**

  评估 ORDER BY 子句和其他排序操作，例如 UNION 查询和联接所需的排序、SELECT DISTINCT 查询和窗口函数。
+ **合并**

  根据从并行操作得到的临时排序结果，来生成最终排序结果。

### UNION、INTERSECT 和 EXCEPT 运算符
<a name="UNION-INTERSECT-and-EXCEPT-operators"></a>

查询计划将以下运算符用于涉及使用 UNION、INTERSECT 和 EXCEPT 进行集合操作的查询。
+ **Subquery**

  用于运行 UNION 查询。
+ **Hash Intersect Distinct**

  用于运行 INTERSECT 查询。
+ **SetOp Except**

  用于运行 EXCEPT（或 MINUS）查询。

### 其他运算符
<a name="other-operators"></a>

以下运算符也经常出现在例行查询的 EXPLAIN 输出中。
+ **唯一**

  消除 SELECT DISTINCT 查询和 UNION 查询的重复项。
+ **限制**

  处理 LIMIT 子句。
+ **窗口**

  运行窗口函数。
+ **结果**

  运行不涉及任何表访问的标量函数。
+ **子计划**

  用于特定的子查询。
+ **Network**

  将临时结果发送到领导节点，以待进一步处理。
+ **实体化**

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

## EXPLAIN 中的联接
<a name="joins-in-EXPLAIN"></a>

查询优化程序使用不同的联接类型来检索表数据，具体取决于查询和基础表的结构。EXPLAIN 输出引用了联接类型、使用的表以及在集群中分布表数据的方式，以描述查询的处理方式。

### 联接类型示例
<a name="join-types"></a>

下面的示例显示了查询优化程序可以使用的不同联接类型。查询计划中使用的联接类型取决于所涉表的物理设计。

#### 示例：对两个表进行哈希联接
<a name="hash-join-two-tables"></a>

以下查询在 CATID 列上将 EVENT 和 CATEGORY 联接起来。CATID 是 CATEGORY 的分配和排序键，但不适用于 EVENT。使用 EVENT 作为外部表并使用 CATEGORY 作为内部表来执行哈希联接。由于 CATEGORY 是较小的表，因此计划程序在查询处理过程中使用 DS\$1BCAST\$1INNER 将其副本广播到计算节点。此示例中的联接成本占计划累计成本的大部分。

```
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 扫描必须等到哈希操作完全完成。

#### 示例：对两个表进行合并联接
<a name="merge-join-two-tables"></a>

以下查询还使用 SELECT \$1，但它在 LISTID 列上联接 SALES 和 LISTING，其中 LISTID 已设置为两个表的分配和排序键。选择合并联接，并且不需要对联接重新分配数据 (DS\$1DIST\$1NONE)。

```
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 必须与合并联接的结果进行哈希联接。同样，哈希联接会产生广播成本。

```
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)
```

#### 示例：联接、聚合和排序
<a name="join-aggregate-and-sort-example"></a>

以下查询运行 SALES 和 EVENT 表的哈希联接，然后执行聚合和排序操作，以考虑分组 SUM 函数和 ORDER BY 子句。初始排序运算符在计算节点上并行运行。然后，Network 运算符将结果发送到领导节点，其中合并运算符生成最终的排序结果。

```
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)
```

### 数据重新分配
<a name="data-redistribution"></a>

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

EXPLAIN 输出还引用内表和外表。首先扫描内部表，并显示在查询计划底部附近。内部表是用来探测匹配项的表。它通常保存在内存中，通常是哈希的源表，如果可能的话，是两者中较小的表。外部表是要与内部表匹配的行的源。它通常是从磁盘读取的。查询优化程序根据最新运行的 ANALYZE 命令中的数据库统计信息选择内表和外表。查询的 FROM 子句中的表顺序并不区分内部表和外部表。

您可以通过查询计划中的以下属性了解数据的移动方式，以便执行查询：
+ **DS\$1BCAST\$1INNER**

  将整个内部表的副本广播到所有计算节点。
+ **DS\$1DIST\$1ALL\$1NONE**

  无需重新分配，因为内部表已经使用 DISTSTYLE ALL 被分配到每个节点。
+ **DS\$1DIST\$1NONE**

  两个表都未重新分配。可以使用并置连接，因为相应的切片联接时不会在节点之间移动数据。
+ **DS\$1DIST\$1INNER**

  内部表重新分配。
+ **DS\$1DIST\$1OUTER**

  外部表重新分配。
+ **DS\$1DIST\$1ALL\$1INNER**

  整个内部表重新分配到单个切片，因为外部表使用 DISTSTYLE ALL。
+ **DS\$1DIST\$1BOTH**

  两个表都重新分配。

# 检查查询计划步骤
<a name="reviewing-query-plan-steps"></a>

您可以通过运行 EXPLAIN 命令来查看查询计划中的步骤。以下示例显示了 SQL 查询并解释了输出。自下而上阅读该查询计划，您可以了解执行该查询所用的每个逻辑操作。有关更多信息，请参阅 [创建和解释查询计划](c-the-query-plan.md)。

```
explain
select eventname, sum(pricepaid) from sales, event
where sales.eventid = event.eventid
group by eventname
order by 2 desc;
```

```
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)
```

作为生成查询计划的一部分，查询优化程序将计划分解为流、分段和步骤。查询优化程序将分解计划，以准备将数据和查询工作负载分配到计算节点。有关流、分段和步骤的更多信息，请参阅[查询计划和执行工作流程](c-query-planning.md)。

下图显示了前面的查询和关联的查询计划。它显示涉及的查询操作如何映射到 Amazon Redshift 用于为计算节点切片生成编译代码的步骤。每个查询计划操作映射到段中的多个步骤，有时映射到流中的多个段。

![\[一个查询及其关联的查询计划映射到三个数据流。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/map-plan-to-streams.png)


在此图中，查询优化程序运行查询计划，如下所示：

1. 在 `Stream 0` 中，查询通过顺序扫描操作运行 `Segment 0`，以扫描 `events` 表。查询继续通过哈希操作运行 `Segment 1`，以为联接中的内部表创建哈希表。

1. 在 `Stream 1` 中，查询通过顺序扫描操作运行 `Segment 2`，以扫描 `sales` 表。它继续使用哈希联接运行 `Segment 2` 以联接表，其中联接列不是分配键和排序键。它再次继续通过哈希聚合来运行 `Segment 2`，以聚合结果。然后，查询使用哈希聚合操作运行 `Segment 3` 来执行未排序的分组聚合函数和排序操作，以评估 ORDER BY 子句和其他排序操作。

1. 在 `Stream 2` 中，查询在 `Segment 4` 和 `Segment 5` 中运行网络操作，以将中间结果发送到领导节点以待进一步处理。

查询的最后一个分段返回数据。如果聚合或对返回集进行排序，则计算节点将各自的中间结果段发送到领导节点。然后，领导节点合并数据，以便将最终结果发送回请求客户端。

有关 EXPLAIN 运算符的更多信息，请参阅[EXPLAIN](r_EXPLAIN.md)。

# 影响查询性能的因素
<a name="c-query-performance"></a>

有很多因素会影响查询性能。数据、集群和数据库操作的以下方面都在查询处理速度方面发挥作用。
+ **节点、处理器或切片的数量** – 一个计算节点分为多个切片。节点越多意味着处理器和切片越多，通过跨各个切片并发运行查询的多个部分，可加快查询的处理速度。但是，节点越多也意味着花费越高，因此，您需要为自己的系统找到成本和性能之间的适当平衡点。有关 Amazon Redshift 集群架构的更多信息，请参阅[数据仓库系统架构](c_high_level_system_architecture.md)。
+ **节点类型** – Amazon Redshift 集群有多种节点类型可以选择。每种节点类型都提供不同的大小和限制，以帮助您适当地扩展集群。节点大小决定集群中每个节点的存储容量、内存、CPU 和价格。有关节点类型的更多信息，请参阅《Amazon Redshift 管理指南》**中的 [Amazon Redshift 集群概述](https://docs.aws.amazon.com/redshift/latest/mgmt/working-with-clusters.html#working-with-clusters-overview)。
+ **数据分配** – Amazon Redshift 根据表的分配方式在计算节点上存储表数据。在执行查询时，查询优化程序根据执行联接和聚合的需要将数据重新分配到计算节点。为表选择正确的分配方式有助于通过在执行联接前将数据放在需要的位置来最大程度地减小重新分配步骤的影响。有关更多信息，请参阅 [用于优化查询的数据分配](t_Distributing_data.md)。
+ **数据排序顺序** – Amazon Redshift 根据表的排序键将表数据按照排序顺序存储在磁盘中。查询优化程序和查询处理器使用有关数据所在位置的信息来减少需要扫描的数据块数，从而提高查询速度。有关更多信息，请参阅 [排序键](t_Sorting_data.md)。
+ **数据集大小** – 集群中的数据量越大，则需要扫描和重新分配的行数也越多，这会降低查询性能。您可以通过定期对数据进行 vacuum 操作和归档以及使用谓词来限制查询数据集来减轻这种影响。
+ **并发操作** – 同时运行多个操作会影响查询性能。每个操作在可用查询队列中都会占用一个或多个插槽，并使用与这些插槽关联的内存。如果其他操作正在运行，则可能没有充足的查询队列槽可用。在这种情况下，查询必须等待有槽回收后才能开始处理。有关创建和配置查询队列的更多信息，请参阅[工作负载管理](cm-c-implementing-workload-management.md)。
+ **查询结构** – 查询的编写也会影响其性能。在满足需求的前提下，请尽量将查询编写为处理和返回尽量少的数据。有关更多信息，请参阅 [设计查询的 Amazon Redshift 最佳实践](c_designing-queries-best-practices.md)。
+ **代码编译**：Amazon Redshift 为每个查询执行计划生成和编译优化的代码。编译代码执行更快，因为它消除了使用解释器的开销。为了最大限度地减少新查询的延迟，同时保留编译代码的性能优势，Amazon Redshift 使用一种称为合成的技术。合成可生成预先存在的逻辑的轻量级排列以便立即处理新查询，同时在后台编译高度优化的查询专用代码。这会将编译从查询执行的关键路径中移除，因此，新查询可以更快地启动并提供与后续运行一致的性能。

  Amazon Redshift 还使用无服务器编译服务将查询编译扩展到 Amazon Redshift 集群的计算资源之外。编译的代码段既本地缓存于集群上，也缓存于一个几乎无限的远程缓存中，该缓存在集群重启后持续存在。相同查询的后续执行可以更快地运行，因为它们可以跳过编译阶段。通过使用可扩展的编译服务，Amazon Redshift 并行编译代码，以提供始终如一的快速性能。

# 查询分析和改进
<a name="c-query-tuning"></a>

从 Amazon Redshift 数据仓库检索信息需要对海量数据运行复杂的查询，这可能需要很长的时间进行处理。为确保尽快处理查询，您可以使用多种工具来确定潜在的性能问题。

**Topics**
+ [查询分析工作流程](c-query-analysis-process.md)
+ [查看查询警报](c-reviewing-query-alerts.md)
+ [分析查询计划](c-analyzing-the-query-plan.md)
+ [分析查询摘要](c-analyzing-the-query-summary.md)
+ [查询性能改进](query-performance-improvement-opportunities.md)
+ [用于优化查询的诊断查询](diagnostic-queries-for-query-tuning.md)

# 查询分析工作流程
<a name="c-query-analysis-process"></a>

如果查询所花费的时间比预期的时间长，请使用以下步骤来确定并纠正可能对查询性能产生负面影响的问题。如果您不确定系统中的哪些查询可能会受益于性能调整，请先运行 [确定最适合优化的查询](identify-queries-that-are-top-candidates-for-tuning.md) 中的诊断查询。

1. 确保您的表格按照最佳实践进行设计。有关更多信息，请参阅 [设计表的 Amazon Redshift 最佳实践](c_designing-tables-best-practices.md)。

1. 查看是否可以删除或归档表中的任何不需要的数据。例如，假设您的查询始终以过去 6 个月的数据为目标，但您的表中有最近 18 个月的数据。在这种情况下，您可以删除或归档较旧的数据，以减少必须扫描和分配的记录数。

1. 对查询中的表运行 [VACUUM](r_VACUUM_command.md) 命令以回收空间并对行进行重新排序。如果未排序的区域很大，并且查询在联接或谓词中使用排序键，则运行 VACUUM 会有所帮助。

1. 对查询中的表运行 [ANALYZE](r_ANALYZE.md) 命令，以确保统计信息是最新的。如果查询中的任何表最近在大小上发生了很大变化，则运行 ANALYZE 会有所帮助。如果运行完整的 ANALYZE 命令需要过长时间，则对某一列运行 ANALYZE，以减少处理时间。这种方法仍会更新表大小的统计数据；表大小在查询计划中是一个很重要的因素。

1. 确保查询在每个类型的客户端（基于客户端使用的连接协议的类型）上都运行一次，以便查询得到编译和缓存。该种方法可加快查询的后续运行速度。有关更多信息，请参阅 [影响查询性能的因素](c-query-performance.md)。

1. 检查 [STL\$1ALERT\$1EVENT\$1LOG](r_STL_ALERT_EVENT_LOG.md) 表来识别和纠正查询中可能存在的问题。有关更多信息，请参阅 [查看查询警报](c-reviewing-query-alerts.md)。

1. 运行 [EXPLAIN](r_EXPLAIN.md) 命令以获取查询计划并使用它优化查询。有关更多信息，请参阅 [分析查询计划](c-analyzing-the-query-plan.md)。

1. 使用 [SVL\$1QUERY\$1SUMMARY](r_SVL_QUERY_SUMMARY.md) 和 [SVL\$1QUERY\$1REPORT](r_SVL_QUERY_REPORT.md) 视图来获取摘要信息并使用它来优化查询。有关更多信息，请参阅 [分析查询摘要](c-analyzing-the-query-summary.md)。

有时，应该快速运行的查询要被迫等到另一个运行时间较长的查询完成。在这种情况下，查询本身可能无需改进，但您可以通过为不同类型的查询创建和使用查询队列来提高整体系统性能。要了解查询的队列等待时间，请参阅[查看查询的队列等待时间](review-queue-wait-times-for-queries.md)。有关配置查询队列的更多信息，请参阅[工作负载管理](cm-c-implementing-workload-management.md)。

# 查看查询警报
<a name="c-reviewing-query-alerts"></a>

要使用 [STL\$1ALERT\$1EVENT\$1LOG](r_STL_ALERT_EVENT_LOG.md) 系统表来识别和纠正查询的潜在性能问题，请按照下列步骤操作：

1. 运行以下命令以确定查询 ID：

   ```
   select query, elapsed, substring
   from svl_qlog
   order by query
   desc limit 5;
   ```

   检查 `substring` 字段中的截断查询文本来确定选择哪些 `query` 值。如果您已多次运行查询，请使用具有较低 `elapsed` 值的行中的 `query` 值。这是已编译的行。如果运行多个查询，则可以增大 LIMIT 子句使用的值，以确保将查询包含在内。

1. 从查询的 STL\$1ALERT\$1EVENT\$1LOG 中选择行：

   ```
   Select * from stl_alert_event_log where query = MyQueryID;               
   ```  
![\[STL_ALERT_EVENT_LOG 的查询结果示例。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/stl_alert_event_log_results.png)

1. 评估查询的结果。使用下面的表找到已发现的任何问题的可能解决方案。
**注意**  
并非所有查询都在 STL\$1ALERT\$1EVENT\$1LOG 中拥有行（仅限存在已发现的问题的查询）。    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/c-reviewing-query-alerts.html)

# 分析查询计划
<a name="c-analyzing-the-query-plan"></a>

运行 [EXPLAIN](r_EXPLAIN.md) 命令以获取查询计划。

在分析查询计划前，应该熟悉如何阅读该计划。如果您不熟悉如何阅读查询计划，建议阅读 [创建和解释查询计划](c-the-query-plan.md) 后再继续。

若要分析查询计划提供的数据，请按照下列步骤操作：

1. 确定成本最高的步骤。继续完成其余步骤时，专注于优化这些步骤。

1. 查看联接类型：
   + **嵌套循环**：此类联接通常因此联接条件被忽略而发生。有关建议的解决方案，请参阅[嵌套循环](query-performance-improvement-opportunities.md#nested-loop)。
   + **哈希和哈希联接**：当联接列不为分配键，也不为排序键时，使用哈希联接来联接表。有关建议的解决方案，请参阅[哈希联接](query-performance-improvement-opportunities.md#hash-join)。
   + **合并联接**：无需更改。

1. 注意哪个表用于内部连接，哪个表用于外部连接。查询引擎通常会为内部连接选择较小的表，为外部连接选择较大的表。如果没有进行此类选择，则您的统计数据可能已过时。有关建议的解决方案，请参阅[表统计数据缺失或过时](query-performance-improvement-opportunities.md#table-statistics-missing-or-out-of-date)。

1. 查看是否有任何高成本的排序操作。如果有，请参阅 [未排序或排序错乱的行](query-performance-improvement-opportunities.md#unsorted-or-mis-sorted-rows) 了解建议的解决方案。

1. 查找具有高成本操作的以下广播运算符：
   + **DS\$1BCAST\$1INNER**：指示将表广播到所有计算节点。这对于小表而言很好，但对于大表来说并不理想。
   + **DS\$1DIST\$1ALL\$1INNER**：表示所有工作负载都位于单个切片上。
   + **DS\$1DIST\$1BOTH**：表示大量重新分配。

   有关这些情况的建议解决方案，请参阅[非最优数据分配](query-performance-improvement-opportunities.md#suboptimal-data-distribution)。

# 分析查询摘要
<a name="c-analyzing-the-query-summary"></a>

要获取比 [EXPLAIN](r_EXPLAIN.md) 生成的查询计划更详细的执行步骤和统计数据，请使用 [SVL\$1QUERY\$1SUMMARY](r_SVL_QUERY_SUMMARY.md) 和 [SVL\$1QUERY\$1REPORT](r_SVL_QUERY_REPORT.md) 系统视图。

SVL\$1QUERY\$1SUMMARY 按流提供查询统计数据。您可以利用它提供的信息发现与高昂成本步骤、耗时步骤及写入磁盘步骤有关的问题。

SVL\$1QUERY\$1REPORT 系统视图为您提供与 SVL\$1QUERY\$1SUMMARY 相似的信息，但其按计算节点切片而非按流提供信息。您可以利用切片级信息检测跨集群的不均匀数据分配（也称作数据分配偏斜），后者会导致某些节点执行更多的工作，从而影响查询的性能。

**Topics**
+ [使用 SVL\$1QUERY\$1SUMMARY 视图](using-SVL-Query-Summary.md)
+ [使用 SVL\$1QUERY\$1REPORT 视图](using-SVL-Query-Report.md)
+ [将查询计划映射到查询摘要](query-plan-summary-map.md)

# 使用 SVL\$1QUERY\$1SUMMARY 视图
<a name="using-SVL-Query-Summary"></a>

要使用 [SVL\$1QUERY\$1SUMMARY](r_SVL_QUERY_SUMMARY.md) 按数据流分析查询摘要信息，请执行以下操作：

1. 运行以下查询以确定查询 ID：

   ```
   select query, elapsed, substring
   from svl_qlog
   order by query
   desc limit 5;
   ```

   检查 `substring` 字段中的截断查询文本来确定哪些 `query` 值代表您的查询。如果您已多次运行查询，请使用具有较低 `elapsed` 值的行中的 `query` 值。这是已编译的行。如果运行多个查询，则可以增大 LIMIT 子句使用的值，以确保将查询包含在内。

1. 从查询的 SVL\$1QUERY\$1SUMMARY 中选择。按流、分段和步骤对结果进行排序：

   ```
   select * from svl_query_summary where query = MyQueryID order by stm, seg, step;
   ```

   下面是示例结果。  
![\[SVL_QUERY_SUMMARY 中与给定查询匹配的行的示例结果。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/svl_query_summary_results.png)

1. 使用 [将查询计划映射到查询摘要](query-plan-summary-map.md) 中的信息将步骤映射到查询计划中的操作。它们应具有大致相同的行和字节值（查询计划中的行 \$1 宽度）。如果没有，请参阅[表统计数据缺失或过时](query-performance-improvement-opportunities.md#table-statistics-missing-or-out-of-date)了解建议的解决方案。

1. 查看在任何步骤中 `is_diskbased` 字段的值是否都为 `t`（真）。哈希、聚合和排序是指在系统没有足够的内存用于查询处理的情况下可能会将数据写入磁盘的运算符。

   如果 `is_diskbased` 为真，请参阅 [分配给查询的内存不足](query-performance-improvement-opportunities.md#insufficient-memory-allocated-to-the-query) 了解建议的解决方案。

1. 查看 `label` 字段值，并查看步骤中是否存在 AGG-DIST-AGG 序列。它的存在表明聚合分两步，其成本高昂。要解决此问题，请将 GROUP BY 子句更改为使用分配键（如果有多个键，则为第一个键）。

1. 查看每个分段的 `maxtime` 值（该值在分段中的所有步骤中相同）。标识具有最高 `maxtime` 值的分段，并查看此分段中的以下运算符的步骤。
**注意**  
较高的 `maxtime` 值并不一定表示分段出现问题。尽管值很高，但该分段可能不需要很长时间来处理。流中的所有分段都开始统一计时。但是，从上游段获得数据前，某些下游段可能无法运行。这可能导致它们看起来需要长时间执行，因为它们的 `maxtime` 值包含等待时间和处理时间。
   + **BCAST 或 DIST**：在这些情况下，`maxtime` 值较大可能是由重新分配大量的行造成的。有关建议的解决方案，请参阅[非最优数据分配](query-performance-improvement-opportunities.md#suboptimal-data-distribution)。
   + **HJOIN（哈希联接）**：如果所涉及的步骤的 `rows` 字段值比查询的最终 RETURN 步骤的 `rows` 值高很多，请参阅 [哈希联接](query-performance-improvement-opportunities.md#hash-join) 了解建议的解决方案。
   + **SCAN/SORT**：查找联接步骤之前的 SCAN、SORT、SCAN、MERGE 步骤序列。此模式表示正在扫描、排序未排序的数据，然后将其与表的排序区域合并。

     查看 SCAN 步骤的行值是否比查询的最终 RETURN 步骤中的行值高很多。此模式表示执行引擎正在扫描稍后将丢弃的行，这样做效率低下。有关建议的解决方案，请参阅[谓词限制性不足](query-performance-improvement-opportunities.md#insufficiently-restrictive-predicate)。

     如果 SCAN 步骤的 `maxtime` 值较高，请参阅 [非最优 WHERE 子句](query-performance-improvement-opportunities.md#suboptimal-WHERE-clause) 了解建议的解决方案。

     如果 SORT 步骤的 `rows` 值较高，请参阅[未排序或排序错乱的行](query-performance-improvement-opportunities.md#unsorted-or-mis-sorted-rows)了解建议的解决方案。

1. 查看最终 RETURN 步骤前 5-10 步的 `rows` 和 `bytes` 值，以了解返回到客户端的数据量。这个过程可以说是一门艺术。

   例如，在下面的示例查询摘要中，第三个 PROJECT 步骤提供了一个 `rows` 值，但没有提供 `bytes` 值。通过在前面的步骤中查找具有相同 `rows` 值的步骤，可以找到同时提供行和字节信息的 SCAN 步骤。

    示例结果如下。  
![\[查询摘要结果中的一行，是 SCAN 步骤，同时包含行和字节信息。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/rows_and_bytes.png)

   如果要返回量异常大的数据，请参阅 [极大结果集](query-performance-improvement-opportunities.md#very-large-result-set) 了解建议的解决方案。

1. 查看任何步骤的 `bytes` 与其他步骤相比是否相对 `rows` 值较高。此模式说明您选择了大量列。有关建议的解决方案，请参阅[大型 SELECT 列表](query-performance-improvement-opportunities.md#large-SELECT-list)。

# 使用 SVL\$1QUERY\$1REPORT 视图
<a name="using-SVL-Query-Report"></a>

要使用 [SVL\$1QUERY\$1REPORT](r_SVL_QUERY_REPORT.md) 按切片分析查询摘要信息，请执行以下操作：

1. 运行以下命令以确定查询 ID：

   ```
   select query, elapsed, substring
   from svl_qlog
   order by query
   desc limit 5;
   ```

   检查 `substring` 字段中的截断查询文本来确定哪些 `query` 值代表您的查询。如果您已多次运行查询，请使用具有较低 `elapsed` 值的行中的 `query` 值。这是已编译的行。如果运行多个查询，则可以增大 LIMIT 子句使用的值，以确保将查询包含在内。

1. 从查询的 SVL\$1QUERY\$1REPORT 中选择。按分段、步骤、elapsed\$1time 和行对结果进行排序：

   ```
   select * from svl_query_report where query = MyQueryID order by segment, step, elapsed_time, rows;
   ```

1. 对于每个步骤，检查以查看处理的所有切片数是否与行数大致相同：  
![\[用于运行查询的数据切片列表。每个切片处理的行数大致相同。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/SVL_QUERY_REPORT_rows.png)

   此外，检查以查看所有切片所花费的时间是否大致相同：  
![\[用于运行查询的数据切片列表。每个切片所需的时间大致相同。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/SVL_QUERY_REPORT_elapsed_time.png)

   这些值存在较大的差异可能表明由于此特定查询的分配方式不佳而导致数据分布偏差。有关建议的解决方案，请参阅[非最优数据分配](query-performance-improvement-opportunities.md#suboptimal-data-distribution)。

# 将查询计划映射到查询摘要
<a name="query-plan-summary-map"></a>

分析查询摘要时，可以将查询计划中的操作映射到查询摘要中的步骤（由标签字段值标识），从而获得更多细节。下表将查询计划操作映射到查询摘要步骤。

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/query-plan-summary-map.html)

# 查询性能改进
<a name="query-performance-improvement-opportunities"></a>

以下是影响 Amazon Redshift 查询性能的一些常见问题，以及诊断和解决这些问题的方法说明。

**Topics**
+ [表统计数据缺失或过时](#table-statistics-missing-or-out-of-date)
+ [嵌套循环](#nested-loop)
+ [哈希联接](#hash-join)
+ [虚影行或未提交的行](#ghost-rows-or-uncommitted-rows)
+ [未排序或排序错乱的行](#unsorted-or-mis-sorted-rows)
+ [非最优数据分配](#suboptimal-data-distribution)
+ [分配给查询的内存不足](#insufficient-memory-allocated-to-the-query)
+ [非最优 WHERE 子句](#suboptimal-WHERE-clause)
+ [谓词限制性不足](#insufficiently-restrictive-predicate)
+ [极大结果集](#very-large-result-set)
+ [大型 SELECT 列表](#large-SELECT-list)

## 表统计数据缺失或过时
<a name="table-statistics-missing-or-out-of-date"></a>

如果表统计信息缺失或过期，您可能会看到以下内容：
+ EXPLAIN 命令结果中存在警告消息。
+ STL\$1ALERT\$1EVENT\$1LOG 中存在缺失统计数据提醒事件。有关更多信息，请参阅 [查看查询警报](c-reviewing-query-alerts.md)。

要修复此问题，请运行 [ANALYZE](r_ANALYZE.md)。

## 嵌套循环
<a name="nested-loop"></a>

如果存在嵌套循环，您可能会在 STL\$1ALERT\$1EVENT\$1LOG 中看到嵌套循环提示事件。此外，您还可以通过运行[确定具有嵌套循环的查询](identify-queries-with-nested-loops.md)中的查询来识别此类事件。有关更多信息，请参阅 [查看查询警报](c-reviewing-query-alerts.md)。

要修复这一问题，请检查查询是否存在交叉联接，尽量将其删除。交叉联接是无联接条件的联接，它会导致对两个表执行笛卡尔积操作。它们通常作为嵌套循环联接运行，这是最慢的可能联接类型。

## 哈希联接
<a name="hash-join"></a>

如果存在哈希联接，您可能会看到以下内容：
+ 查询计划中的哈希和哈希联接操作。有关更多信息，请参阅 [分析查询计划](c-analyzing-the-query-plan.md)。
+ SVL\$1QUERY\$1SUMMARY 中 maxtime 值最大的段中的 HJOIN 步骤。有关更多信息，请参阅 [使用 SVL\$1QUERY\$1SUMMARY 视图](using-SVL-Query-Summary.md)。

要修复这一问题，您可以采取以下几种方法：
+ 重写查询，尽可能使用合并联接。为此，您可以指定既是分配键又是排序键的联接列。
+ 如果 SVL\$1QUERY\$1SUMMARY 中 HJOIN 步骤的行数字段值较查询中最终 RETURN 步骤中的行数值大得多，请确认您能否重写查询以基于唯一的列进行联接。当查询未联接唯一列（如主键）时，该列会增加联接中涉及的行数。

## 虚影行或未提交的行
<a name="ghost-rows-or-uncommitted-rows"></a>

如果存在虚影行或未提交的行，您可能会在 STL\$1ALERT\$1EVENT\$1LOG 中看到一个提示事件，指示虚影行过多。有关更多信息，请参阅 [查看查询警报](c-reviewing-query-alerts.md)。

要修复这一问题，您可以采取以下几种方法：
+ 检查 Amazon Redshift 控制台的**加载**选项卡，以便对任何查询表进行活动加载操作。如果您发现有处于活动状态的加载操作，请等待这些操作完成，然后再执行操作。
+ 如果没有活动加载操作，请对查询表运行 [VACUUM](r_VACUUM_command.md) 以移除已删除的行。

## 未排序或排序错乱的行
<a name="unsorted-or-mis-sorted-rows"></a>

如果存在未排序或排序错误的行，您可能会在 STL\$1ALERT\$1EVENT\$1LOG 中看到一个非常有选择性的筛选提示事件。有关更多信息，请参阅 [查看查询警报](c-reviewing-query-alerts.md)。

您也可以运行[确定具有数据偏斜或未排序行的表](identify-tables-with-data-skew-or-unsorted-rows.md)中的查询以检查查询中的任意表是否包含大片未排序的区域。

要修复这一问题，您可以采取以下几种方法：
+ 对查询表运行 [VACUUM](r_VACUUM_command.md) 以重新排序行。
+ 检查查询表的排序键，看看有无可以改进之处。在进行任何更改之前，请务必权衡查询的性能及其他重要查询和系统的整体性能。有关更多信息，请参阅 [排序键](t_Sorting_data.md)。

## 非最优数据分配
<a name="suboptimal-data-distribution"></a>

如果数据分配不佳，您可能会看到以下内容：
+ STL\$1ALERT\$1EVENT\$1LOG 中存在顺序执行、大型广播或大型分配提醒事件。有关更多信息，请参阅 [查看查询警报](c-reviewing-query-alerts.md)。
+ 对于给定步骤，处理的切片数与行数相差较大。有关更多信息，请参阅 [使用 SVL\$1QUERY\$1REPORT 视图](using-SVL-Query-Report.md)。
+ 对于给定步骤，切片的处理时间相差较大。有关更多信息，请参阅 [使用 SVL\$1QUERY\$1REPORT 视图](using-SVL-Query-Report.md)。

如果上述条件均未满足，您还可以查看查询中是否有任意表存在数据偏斜（运行[确定具有数据偏斜或未排序行的表](identify-tables-with-data-skew-or-unsorted-rows.md)中的查询）。

要修复这一问题，请查看查询中表的分布方式，看看有无任何可以改进之处。在进行任何更改之前，请务必权衡查询的性能及其他重要查询和系统的整体性能。有关更多信息，请参阅 [用于优化查询的数据分配](t_Distributing_data.md)。

## 分配给查询的内存不足
<a name="insufficient-memory-allocated-to-the-query"></a>

如果为查询分配到的内存不足，您可能会看到 SVL\$1QUERY\$1SUMMARY 中存在一个 `is_diskbased` 值为真的步骤。有关更多信息，请参阅 [使用 SVL\$1QUERY\$1SUMMARY 视图](using-SVL-Query-Summary.md)。

要修复这一问题，请临时增加查询使用的查询槽的数目，以向其分配更多的内存。工作负载管理 (WLM) 在查询队列中预留与为查询设置的并发级别相等的槽数。例如，并发级别为 5 的队列拥有 5 个槽。分配给队列的内存平均分配到每个槽。将多个槽分配给一个查询可使该查询访问所有这些槽的内存。有关如何临时增加查询的插槽的更多信息，请参阅[wlm\$1query\$1slot\$1count](r_wlm_query_slot_count.md)。

## 非最优 WHERE 子句
<a name="suboptimal-WHERE-clause"></a>

如果 WHERE 子句导致表扫描过多，您可能会在分段中看到在 SVL\$1QUERY\$1SUMMARY 中具有最高 `maxtime` 值的 SCAN 步骤。有关更多信息，请参阅 [使用 SVL\$1QUERY\$1SUMMARY 视图](using-SVL-Query-Summary.md)。

要修复这一问题，请根据最大的表的主排序列向查询添加 WHERE 子句。这种方法有助于尽量减少扫描时间。有关更多信息，请参阅 [设计表的 Amazon Redshift 最佳实践](c_designing-tables-best-practices.md)。

## 谓词限制性不足
<a name="insufficiently-restrictive-predicate"></a>

如果您的查询具有限制性不足的谓词，您可能会在分段中看到在 SVL\$1QUERY\$1SUMMARY 中具有最高 `maxtime` SCAN 步骤，且它的 `rows` 值比查询的最终 RETURN 步骤中的 `rows` 值高很多。有关更多信息，请参阅 [使用 SVL\$1QUERY\$1SUMMARY 视图](using-SVL-Query-Summary.md)。

若要解决此问题，请尝试向查询添加谓词或使现有谓词更具限制性，以缩小输出范围。

## 极大结果集
<a name="very-large-result-set"></a>

如果查询返回极大的结果集，请考虑重写查询，使用 [UNLOAD](r_UNLOAD.md) 将结果写入 Amazon S3。这种方法可充分利用并行处理的优势，从而提高 RETURN 步骤的性能。有关检查极大结果集的更多信息，请参阅[使用 SVL\$1QUERY\$1SUMMARY 视图](using-SVL-Query-Summary.md)。

## 大型 SELECT 列表
<a name="large-SELECT-list"></a>

如果您的查询有一个异常大的 SELECT 列表，您可能会看到 SVL\$1QUERY\$1SUMMARY 中的任何步骤的 `bytes` 值（与其他步骤相比）相对于 `rows` 值较高。`bytes` 值较大说明您选择了大量列。有关更多信息，请参阅 [使用 SVL\$1QUERY\$1SUMMARY 视图](using-SVL-Query-Summary.md)。

要解决此问题，请查看您正在选择的列，并查看是否可以删除任何列。

# 用于优化查询的诊断查询
<a name="diagnostic-queries-for-query-tuning"></a>

使用以下查询发现与可能影响查询性能的查询或基础表有关的问题。我们建议将这些查询与[查询分析和改进](c-query-tuning.md)中讨论的查询优化过程结合使用。

**注意**  
这些查询适用于 Amazon Redshift 预置集群。这些查询不适用于 Redshift Serverless 工作组。

**Topics**
+ [确定最适合优化的查询](identify-queries-that-are-top-candidates-for-tuning.md)
+ [确定具有数据偏斜或未排序行的表](identify-tables-with-data-skew-or-unsorted-rows.md)
+ [确定具有嵌套循环的查询](identify-queries-with-nested-loops.md)
+ [查看查询的队列等待时间](review-queue-wait-times-for-queries.md)
+ [按表查看查询警报](review-query-alerts-by-table.md)
+ [确定统计数据缺失的表](identify-tables-with-missing-statistics.md)

# 确定最适合优化的查询
<a name="identify-queries-that-are-top-candidates-for-tuning"></a>

以下查询标识了过去 7 天内运行的前 50 个最耗时的语句。您可以使用结果来识别需要非常长时间的查询。您也可以确定经常运行的查询（在结果集中多次出现的查询）。通常可以优化此类查询以提高系统性能。

此外，此查询还提供与每个所发现的查询关联的提醒事件计数。这些提醒提供详细信息，供您用于提高查询的性能。有关更多信息，请参阅 [查看查询警报](c-reviewing-query-alerts.md)。

```
select trim(database) as db, count(query) as n_qry, 
max(substring (qrytext,1,80)) as qrytext, 
min(run_minutes) as "min" , 
max(run_minutes) as "max", 
avg(run_minutes) as "avg", sum(run_minutes) as total,  
max(query) as max_query_id, 
max(starttime)::date as last_run, 
sum(alerts) as alerts, aborted
from (select userid, label, stl_query.query, 
trim(database) as database, 
trim(querytxt) as qrytext, 
md5(trim(querytxt)) as qry_md5, 
starttime, endtime, 
(datediff(seconds, starttime,endtime)::numeric(12,2))/60 as run_minutes,     
alrt.num_events as alerts, aborted 
from stl_query 
left outer join 
(select query, 1 as num_events from stl_alert_event_log group by query ) as alrt 
on alrt.query = stl_query.query
where userid <> 1 and starttime >=  dateadd(day, -7, current_date)) 
group by database, label, qry_md5, aborted
order by total desc limit 50;
```

# 确定具有数据偏斜或未排序行的表
<a name="identify-tables-with-data-skew-or-unsorted-rows"></a>

以下查询标识数据分配不均匀（数据偏斜）或未排序行百分比较高的表。

`skew` 值较低表明表数据分配适当。如果表的 `skew` 值达到 4.00 或以上，可以考虑修改其数据分配方式。有关更多信息，请参阅 [非最优数据分配](query-performance-improvement-opportunities.md#suboptimal-data-distribution)。

如果表的 `pct_unsorted` 值大于 20%，可以考虑运行 [VACUUM](r_VACUUM_command.md) 命令。有关更多信息，请参阅 [未排序或排序错乱的行](query-performance-improvement-opportunities.md#unsorted-or-mis-sorted-rows)。

还应检查每个表的 `mbytes` 和 `pct_of_total` 值。这些列标识表的大小及表占用的原始磁盘空间比例。原始磁盘空间包括 Amazon Redshift 保留供内部使用的空间，因此它大于名义磁盘容量（可供用户使用的磁盘空间量）。使用这些信息可验证可用磁盘空间等于最大表大小的 2.5 倍或以上。如果使用此空间，系统可以在处理复杂查询时将临时结果写入磁盘。

```
select trim(pgn.nspname) as schema, 
trim(a.name) as table, id as tableid, 
decode(pgc.reldiststyle,0, 'even',1,det.distkey ,8,'all') as distkey, dist_ratio.ratio::decimal(10,4) as skew, 
det.head_sort as "sortkey", 
det.n_sortkeys as "#sks", b.mbytes,  
decode(b.mbytes,0,0,((b.mbytes/part.total::decimal)*100)::decimal(5,2)) as pct_of_total, 
decode(det.max_enc,0,'n','y') as enc, a.rows, 
decode( det.n_sortkeys, 0, null, a.unsorted_rows ) as unsorted_rows , 
decode( det.n_sortkeys, 0, null, decode( a.rows,0,0, (a.unsorted_rows::decimal(32)/a.rows)*100) )::decimal(5,2) as pct_unsorted 
from (select db_id, id, name, sum(rows) as rows, 
sum(rows)-sum(sorted_rows) as unsorted_rows 
from stv_tbl_perm a 
group by db_id, id, name) as a 
join pg_class as pgc on pgc.oid = a.id
join pg_namespace as pgn on pgn.oid = pgc.relnamespace
left outer join (select tbl, count(*) as mbytes 
from stv_blocklist group by tbl) b on a.id=b.tbl
inner join (select attrelid, 
min(case attisdistkey when 't' then attname else null end) as "distkey",
min(case attsortkeyord when 1 then attname  else null end ) as head_sort , 
max(attsortkeyord) as n_sortkeys, 
max(attencodingtype) as max_enc 
from pg_attribute group by 1) as det 
on det.attrelid = a.id
inner join ( select tbl, max(mbytes)::decimal(32)/min(mbytes) as ratio 
from (select tbl, trim(name) as name, slice, count(*) as mbytes
from svv_diskusage group by tbl, name, slice ) 
group by tbl, name ) as dist_ratio on a.id = dist_ratio.tbl
join ( select sum(capacity) as  total
from stv_partitions where part_begin=0 ) as part on 1=1
where mbytes is not null 
order by  mbytes desc;
```

# 确定具有嵌套循环的查询
<a name="identify-queries-with-nested-loops"></a>

以下查询标识已为嵌套循环记录提示事件的查询。有关如何修复嵌套循环条件的信息，请参阅[嵌套循环](query-performance-improvement-opportunities.md#nested-loop)。

```
select query, trim(querytxt) as SQL, starttime 
from stl_query 
where query in (
select distinct query 
from stl_alert_event_log 
where event like 'Nested Loop Join in the query plan%') 
order by starttime desc;
```

# 查看查询的队列等待时间
<a name="review-queue-wait-times-for-queries"></a>

以下查询显示了最近的查询在运行之前等待查询队列中可用槽的时间。如果等待时间较长，可能需要修改查询队列配置，以获得更高的吞吐量。有关更多信息，请参阅 [实施手动 WLM](cm-c-defining-query-queues.md)。

```
select trim(database) as DB , w.query, 
substring(q.querytxt, 1, 100) as querytxt,  w.queue_start_time, 
w.service_class as class, w.slot_count as slots, 
w.total_queue_time/1000000 as queue_seconds, 
w.total_exec_time/1000000 exec_seconds, (w.total_queue_time+w.total_Exec_time)/1000000 as total_seconds 
from stl_wlm_query w 
left join stl_query q on q.query = w.query and q.userid = w.userid 
where w.queue_start_Time >= dateadd(day, -7, current_Date) 
and w.total_queue_Time > 0  and w.userid >1   
and q.starttime >= dateadd(day, -7, current_Date) 
order by w.total_queue_time desc, w.queue_start_time desc limit 35;
```

# 按表查看查询警报
<a name="review-query-alerts-by-table"></a>

以下查询标识记录了提示事件的表，并标识了最频繁引发的提示类型。

如果所确定的表的行的 `minutes` 值较大，请检查该表以查看是否需要对其执行日常维护（如对其运行 [ANALYZE](r_ANALYZE.md) 或 [VACUUM](r_VACUUM_command.md)）。

如果某一行的 `count` 值高但 `table` 值为 null，则对 STL\$1ALERT\$1EVENT\$1LOG 运行查询以获得相关的 `event` 值，从而调查如此频繁引发提示的原因。

```
select trim(s.perm_table_name) as table, 
(sum(abs(datediff(seconds, s.starttime, s.endtime)))/60)::numeric(24,0) as minutes, trim(split_part(l.event,':',1)) as event,  trim(l.solution) as solution, 
max(l.query) as sample_query, count(*) 
from stl_alert_event_log as l 
left join stl_scan as s on s.query = l.query and s.slice = l.slice 
and s.segment = l.segment and s.step = l.step
where l.event_time >=  dateadd(day, -7, current_Date) 
group by 1,3,4 
order by 2 desc,6 desc;
```

# 确定统计数据缺失的表
<a name="identify-tables-with-missing-statistics"></a>

以下查询提供了对缺少统计信息的表运行的查询的计数。如果此查询返回任何行，请查看 `plannode` 值来确定受影响的表，然后对其运行 [ANALYZE](r_ANALYZE.md)。

```
select substring(trim(plannode),1,100) as plannode, count(*) 
from stl_explain 
where plannode like '%missing statistics%' 
group by plannode 
order by 2 desc;
```

# 查询故障排除
<a name="queries-troubleshooting"></a>

本节提供快速参考，帮助您识别和解决一些在使用 Amazon Redshift 查询时可能遇到的最常见问题和最严重问题。

**Topics**
+ [连接失败](queries-troubleshooting-connection-fails.md)
+ [查询挂起](queries-troubleshooting-query-hangs.md)
+ [查询耗时过长](queries-troubleshooting-query-takes-too-long.md)
+ [加载失败](queries-troubleshooting-load-fails.md)
+ [加载耗时过长](queries-troubleshooting-load-takes-too-long.md)
+ [加载数据不正确](queries-troubleshooting-load-data-incorrect.md)
+ [设置 JDBC 提取大小参数](set-the-JDBC-fetch-size-parameter.md)

这些建议为您提供进行故障排除的起点。您还可以参阅以下资源以获取更多详细信息。

有关 Amazon Redshift 功能中可能影响应用程序的行为更改的信息，请参阅[行为更改](https://docs.aws.amazon.com/redshift/latest/mgmt/behavior-changes.html)。
+ [访问 Amazon Redshift 集群和数据库](https://docs.aws.amazon.com/redshift/latest/mgmt/using-rs-tools.html)
+ [自动表优化](t_Creating_tables.md)
+ [在 Amazon Redshift 中加载数据](t_Loading_data.md)
+ [教程：从 Amazon S3 加载数据](tutorial-loading-data.md)

# 连接失败
<a name="queries-troubleshooting-connection-fails"></a>

由于以下原因，您的查询连接可能会失败。我们建议采用以下故障排除方法。

**客户端无法连接到服务器**  
如果您使用了 SSL 或服务器证书，请在开始排查连接问题之前将其删除以降低复杂性。待您找到解决方案后，再重新添加 SSL 或服务器证书。有关更多信息，请转至《Amazon Redshift 管理指南》**中的[配置连接的安全选项](https://docs.aws.amazon.com/redshift/latest/mgmt/connecting-ssl-support.html)。

**连接被拒绝**  
通常情况下，当您收到提示连接建立失败的错误消息时，意味着您没有访问集群的权限。有关更多信息，请转至《Amazon Redshift 管理指南》**中的[连接被拒绝或失败](https://docs.aws.amazon.com/redshift/latest/mgmt/connecting-refusal-failure-issues.html)。

# 查询挂起
<a name="queries-troubleshooting-query-hangs"></a>

由于以下原因，您的查询可能会挂起或停止响应。我们建议采用以下故障排除方法。

**到数据库的连接中断**  
减小最大传输单元 (MTU) 的大小。MTU 大小确定可通过网络连接在单个以太网帧中传输的数据包的最大大小（以字节为单位）。有关更多信息，请转至《Amazon Redshift 管理指南》**中的[至数据库的连接被删除](https://docs.aws.amazon.com/redshift/latest/mgmt/connecting-drop-issues.html)。

**到数据库的连接超时**  
在运行 COPY 命令等较长的查询时，客户端到数据库的连接会挂起或超时。此时，您可能会发现，Amazon Redshift 控制台显示查询已完成，而客户端工具仍然显示正在运行查询。查询结果可能会丢失或不完整，具体取决于连接停止的时间。如果中间网络组件终止空闲连接，则会出现这种情况。有关更多信息，请转至《Amazon Redshift 管理指南》**中的[防火墙超时问题](https://docs.aws.amazon.com/redshift/latest/mgmt/connecting-firewall-guidance.html)。

**ODBC 出现客户端内存不足错误**  
如果您的客户端应用程序使用 ODBC 连接，并且您的查询创建的结果集太大，无法存储到内存中，则可使用光标将结果集流式传输到客户端应用程序。有关更多信息，请参阅[DECLARE](declare.md)和[使用游标时的性能注意事项](declare.md#declare-performance)。

**JDBC 出现客户端内存不足错误**  
因此，尝试通过 JDBC 连接检索大型结果集时，可能会遇到客户端内存不足错误。有关更多信息，请参阅 [设置 JDBC 提取大小参数](set-the-JDBC-fetch-size-parameter.md)。

**可能存在死锁**  
如果存在潜在死锁，请尝试以下操作：
+ 查看 [STV\$1LOCKS](r_STV_LOCKS.md) 和 [STL\$1TR\$1CONFLICT](r_STL_TR_CONFLICT.md) 系统表，查找涉及对多个表的更新的冲突。
+ 使用 [PG\$1CANCEL\$1BACKEND](PG_CANCEL_BACKEND.md) 函数可取消一个或多个冲突查询。
+ 使用 [PG\$1TERMINATE\$1BACKEND](PG_TERMINATE_BACKEND.md) 函数终止会话，这会强制使已终止会话中的任意当前正在运行的查询释放所有死锁并回滚事务。
+ 仔细计划并发写入操作。有关更多信息，请参阅 [管理并发写入操作](c_Concurrent_writes.md)。

# 查询耗时过长
<a name="queries-troubleshooting-query-takes-too-long"></a>

由于以下原因，您的查询可能需要太长时间。我们建议采用以下故障排除方法。

**表未优化**  
设置表的排序键、分配方式和压缩编码，以充分利用并行处理。有关更多信息，请参阅[自动表优化](t_Creating_tables.md)。

**查询正在写入到磁盘**  
您的查询可能至少在部分查询执行中写入磁盘。有关更多信息，请参阅 [查询性能改进](query-performance-improvement-opportunities.md)。

**查询必须等待其他查询完成**  
您可以通过创建查询队列并将不同类型的查询分配给适当的队列来提高整体系统性能。有关更多信息，请参阅 [工作负载管理](cm-c-implementing-workload-management.md)。

**查询未优化**  
分析解释计划，找到重写查询或优化数据库的机会。有关更多信息，请参阅 [创建和解释查询计划](c-the-query-plan.md)。

**查询需要更多内存才能运行**  
如果特定查询需要更多内容，您可以通过增大 [wlm\$1query\$1slot\$1count](r_wlm_query_slot_count.md) 来增加可用内存。

**数据库需要运行 VACUUM 命令**  
当添加、删除或修改大量行时，运行 VACUUM 命令，除非按排序键顺序加载数据。VACUUM 命令会重新组织您的数据，以维持排序顺序和还原性能。有关更多信息，请参阅 [对表执行 vacuum 操作](t_Reclaiming_storage_space202.md)。

## 对长时间运行的查询进行故障排除的其他资源
<a name="queries-troubleshooting-cross-refs"></a>

以下是有助于优化查询的系统视图主题和其他文档部分：
+ [STV\$1INFLIGHT](r_STV_INFLIGHT.md) 系统视图显示集群上正在运行哪些查询。将它与 [STV\$1RECENTS](r_STV_RECENTS.md) 一起使用有助于确定哪些查询当前正在运行或最近已完成。
+ [SYS\$1QUERY\$1HISTORY](SYS_QUERY_HISTORY.md) 对故障排除很有用。它可显示 DDL 和 DML 查询的相关属性，例如它们的当前状态（如 `running` 或 `failed`）、每个查询运行所花的时间，以及查询是否在并发扩展集群上运行。
+ [STL\$1QUERYTEXT](r_STL_QUERYTEXT.md) 捕获 SQL 命令的查询文本。此外，[SVV\$1QUERY\$1INFLIGHT](r_SVV_QUERY_INFLIGHT.md)（可将 STL\$1QUERYTEXT 联接到 STV\$1INFLIGHT）显示了更多的查询元数据。
+ 事务锁定冲突可能是查询性能问题的根源。有关当前持有表锁的事务的信息，请参阅[SVV\$1TRANSACTIONS](r_SVV_TRANSACTIONS.md)。
+ [确定最适合优化的查询](https://docs.aws.amazon.com/redshift/latest/dg/diagnostic-queries-for-query-tuning.html#identify-queries-that-are-top-candidates-for-tuning)提供了一个故障排除查询，可帮助您确定最近运行的哪些查询最耗时。这可以帮助您将精力集中在需要改进的查询上。
+ 如果您想进一步探索查询管理并了解如何管理查询队列，[工作负载管理](cm-c-implementing-workload-management.md)显示了如何实现。工作负载管理是一项高级功能，在大多数情况下，我们建议使用自动工作负载管理。

# 加载失败
<a name="queries-troubleshooting-load-fails"></a>

由于以下原因，您的数据加载可能会失败。我们建议采用以下故障排除方法。

**数据源在不同的 AWS 区域**  
预设情况下，COPY 命令中指定的 Amazon S3 桶或 Amazon DynamoDB 表必须位于集群所在的 AWS 区域。如果您的数据和集群位于不同的区域，您将收到如下所示的错误消息：

```
The bucket you are attempting to access must be addressed using the specified endpoint.
```

尽量确保集群和数据源位于同一区域。您可以通过在 COPY 命令中使用 [REGION](copy-parameters-data-source-s3.md#copy-region) 选项来指定其他区域。

**注意**  
如果集群和数据源位于不同的 AWS 区域，则会产生数据传输费用。此外，您还会经历更高的延迟。

**COPY 命令失败**  
查询 STL\$1LOAD\$1ERRORS 以发现在特定加载期间发生的错误。有关更多信息，请参阅 [STL\$1LOAD\$1ERRORS](r_STL_LOAD_ERRORS.md)。

# 加载耗时过长
<a name="queries-troubleshooting-load-takes-too-long"></a>

由于以下原因，您的加载操作可能需要太长时间。我们建议采用以下故障排除方法。

**COPY 从单个文件中加载数据**  
将加载数据拆分为多个文件。如果从一个大型文件加载所有数据，Amazon Redshift 必须执行序列化加载，这样速度很慢。文件数应为集群中切片数量的倍数，且文件大小应大致相同，压缩后介于 1 MB 和 1 GB 之间。有关更多信息，请参阅 [设计查询的 Amazon Redshift 最佳实践](c_designing-queries-best-practices.md)。

**加载操作使用多个 COPY 命令**  
如果您使用多个并发 COPY 命令从多个文件加载一个表，会强制 Amazon Redshift 执行序列化加载，这样速度慢得多。在这种情况下，请使用单个 COPY 命令。

# 加载数据不正确
<a name="queries-troubleshooting-load-data-incorrect"></a>

您的 COPY 操作可能通过以下方式加载不正确的数据。我们建议采用以下故障排除方法。

**加载错误的文件**  
使用对象前缀指定数据文件可能会导致读取不需要的文件。因此，请使用清单文件准确指定要加载的文件。有关更多信息，请参阅 COPY 命令中的 [copy_from_s3_manifest_file](copy-parameters-data-source-s3.md#copy-manifest-file) 选项和 COPY 示例中的 [Example: COPY from Amazon S3 using a manifest](r_COPY_command_examples.md#copy-command-examples-manifest)。

# 设置 JDBC 提取大小参数
<a name="set-the-JDBC-fetch-size-parameter"></a>

默认情况下，Redshift JDBC 驱动程序使用环形缓冲区来高效管理内存，并防止出现内存不足错误。仅当明确禁用环形缓冲区时，提取大小参数才适用。有关更多信息，请查看[链接](https://docs.aws.amazon.com/redshift/latest/mgmt/jdbc20-configuration-options.html#jdbc20-enablefetchringbuffer-option)。在此配置中，您应设置提取大小以控制每个批次中检索的行数。

在以下情况下使用提取大小参数：
+ 您需要对基于行的批处理实施精细控制
+ 使用需要传统提取大小行为的旧式应用程序

在已禁用环形缓冲区的情况下，JDBC 驱动程序会默认一次性收集查询的所有结果。返回大型结果集的查询可能会占用过量内存。要分批而非一次性检索结果集，请在应用程序中设置 JDBC 提取大小参数。

**注意**  
ODBC 不支持提取大小。

为获得最佳性能，请将提取大小设置为不会导致内存不足错误的最大值。较小的提取大小值会导致更多的服务器通信，从而延长执行时间。服务器会预留资源，包括 WLM 查询槽和关联内存，直到客户端检索到整个结果集或查询取消为止。如果适当优化提取大小，则可以更快释放这些资源，使其能够供其他查询使用。

**注意**  
如果需要提取大型数据集，建议使用 [UNLOAD](https://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD.html) 语句将数据传输到 Amazon S3。使用 UNLOAD 时，计算节点并行工作，以加快数据的传输。

有关设置 JDBC 提取大小参数的更多信息，请参阅 PostgreSQL 文档中的[基于光标获取结果](https://jdbc.postgresql.org/documentation/query/#getting-results-based-on-a-cursor)。