

 从补丁 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/)。

# 在 Amazon Redshift 中加载数据
<a name="t_Loading_data"></a>

可以通过多种方法将数据加载到 Amazon Redshift 数据库中。要加载的一个常见数据来源是 Amazon S3 文件。下表总结了从 Amazon S3 来源开始加载时要使用的一些方法。


| 要使用的方法 | 说明 | 何时需要方法 | 
| --- | --- | --- | 
| COPY command | 运行批量文件摄取，从 Amazon S3 文件中加载数据。此方法会利用 Amazon Redshift 的并行处理功能。有关更多信息，请参阅 [使用 COPY 命令加载表](t_Loading_tables_with_the_COPY_command.md)。 | 当需要满足基本数据加载要求（用于手动启动批量文件摄取）时，应使用此方法。此方法主要用于自定义和第三方文件摄取管道或一次性或临时文件摄取工作负载。 | 
| COPY... CREATE JOB 命令（自动复制） | 在所跟踪的 Amazon S3 路径上创建新文件时，自动运行您的 COPY 命令。有关更多信息，请参阅 [创建 S3 事件集成以自动从 Amazon S3 存储桶复制文件](loading-data-copy-job.md)。 | 当在 Amazon S3 上创建新文件时，如果文件摄取管道需要自动摄取数据，则应使用此方法。Amazon Redshift 保持跟踪摄取的文件，来防止数据重复。此方法要求由 Amazon S3 存储桶拥有者进行配置。 | 
| 从数据湖查询中加载 | 创建外部表来对 Amazon S3 文件运行数据湖查询，然后运行 INSERT INTO 命令将来自数据湖查询的结果加载到本地表中。有关更多信息，请参阅 [Redshift Spectrum 的外部表](c-spectrum-external-tables.md)。 | 在以下任何情况下，应使用此方法： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/t_Loading_data.html) | 
| 您可以考虑的其它方法 | 
| 流式摄取  | 流式摄取以低延迟、高速度的方式，将流数据从 Amazon Kinesis Data Streams 和 Amazon Managed Streaming for Apache Kafka 摄取到 Amazon Redshift 预置或 Redshift Serverless 实体化视图中。有关更多信息，请参阅[开始使用 Amazon Kinesis Data Streams 流式摄取](materialized-view-streaming-ingestion-getting-started.md)和[从 Apache Kafka 源进行流式摄取入门](materialized-view-streaming-ingestion-getting-started-MSK.md)。 | 对于数据首先流式传输到 Amazon S3 上的文件，然后从 Amazon S3 加载等此类应用场景，应考虑使用此方法。如果不需要在 Amazon S3 上保留数据，则通常可以考虑将数据直接流式传输到 Amazon Redshift 中。 | 
| 运行数据湖查询 | 直接从数据湖表运行查询，而不是将表的内容摄取到本地表中。有关更多信息，请参阅 [Amazon Redshift Spectrum](c-using-spectrum.md)。 | 当应用场景不要求在 Amazon Redshift 中执行本地表查询时，应使用此方法。 | 
| 使用 Amazon Redshift 查询编辑器 v2 进行批量加载 | 您可以在 Amazon Redshift 查询编辑器 v2 中，直观地准备和运行批量文件摄取工作负载。有关更多信息，请参阅《Amazon Redshift 管理指南》**中的[从 S3 加载数据](https://docs.aws.amazon.com/redshift/latest/mgmt/query-editor-v2-loading.html#query-editor-v2-loading-data)。 | 当您想让查询编辑器 v2 来准备 COPY 语句，并且您想要一个可视化工具来简化 COPY 语句的准备过程时，应使用此方法。 | 
| 使用 Amazon Redshift 查询编辑器 v2 从本地文件加载数据 | 您可以直接将文件从桌面上传到 Amazon Redshift 表中，而无需手动将文件上传到 Amazon S3。有关更多信息，请参阅《Amazon Redshift 管理指南》**中的[从本地文件设置和工作流加载数据](https://docs.aws.amazon.com/redshift/latest/mgmt/query-editor-v2-loading.html#query-editor-v2-loading-data-local)。 | 当您需要从本地计算机快速加载文件来进行一次性查询时，应使用此方法。使用这种方法，Amazon Redshift 查询编辑器 v2 会临时将文件存储在客户拥有的 Amazon S3 存储桶上，并使用此 Amazon S3 路径运行复制命令。 | 

COPY 命令是最高效的加载表的方式。您也可以使用 INSERT 命令将数据添加到您的表中，尽管这与使用 COPY 命令相比的效率低得多。COPY 命令能够同时从多个数据文件或多个数据流读取。Amazon Redshift 会将工作负载分配到 Amazon Redshift 节点，并且并行执行加载操作，包括对行进行排序和跨节点切片分配数据。

**注意**  
Amazon Redshift Spectrum 外部表为只读。您无法对外部表进行 COPY 或 INSERT。

要访问其它 AWS 资源上的数据，Amazon Redshift 必须有权访问这些资源和有权执行访问数据所需的操作。您可使用 AWS Identity and Access Management（IAM）将用户拥有的访问权限限制为 Amazon Redshift 资源和数据。

在初始数据加载后，如果您添加、修改或删除大量数据，则应随后运行 VACUUM 命令，以便识别您的数据并在数据删除后回收空间。您还应该运行 ANALYZE 命令来更新表统计数据。

**Topics**
+ [使用 COPY 命令加载表](t_Loading_tables_with_the_COPY_command.md)
+ [创建 S3 事件集成以自动从 Amazon S3 存储桶复制文件](loading-data-copy-job.md)
+ [使用 DML 命令加载表](t_Updating_tables_with_DML_commands.md)
+ [执行深层复制](performing-a-deep-copy.md)
+ [分析表](t_Analyzing_tables.md)
+ [对表执行 vacuum 操作](t_Reclaiming_storage_space202.md)
+ [管理并发写入操作](c_Concurrent_writes.md)
+ [教程：从 Amazon S3 加载数据](tutorial-loading-data.md)

# 使用 COPY 命令加载表
<a name="t_Loading_tables_with_the_COPY_command"></a>

COPY 命令使用 Amazon Redshift 大规模并行处理 (MPP) 架构从 Amazon S3 上的文件、DynamoDB 表或者来自一个或多个远程主机的文本输出并行读取和加载数据。

在学习 COPY 命令的所有选项之前，我们建议先学习用于加载 Amazon S3 数据的基本选项。《Amazon Redshift 入门指南》**演示了如何使用默认 IAM 角色轻松通过 COPY 命令来加载 Amazon S3 数据。有关详细信息，请参阅[步骤 4：将数据从 Amazon S3 加载到 Amazon Redshift](https://docs.aws.amazon.com/redshift/latest/gsg/rs-gsg-create-sample-db.html)。

**注意**  
我们强烈建议使用 COPY 命令加载大量数据。使用单个 INSERT 语句填充表可能过于缓慢。此外，如果您的数据在其他 Amazon Redshift 数据库表中已经存在，请使用 INSERT INTO ... SELECT 或 CREATE TABLE AS 来提高性能。有关更多信息，请参阅 [INSERT](r_INSERT_30.md) 或 [CREATE TABLE AS](r_CREATE_TABLE_AS.md)。

要从另一 AWS 资源加载数据，Amazon Redshift 必须有权访问相应资源和执行所需操作。

若要授予或撤消使用 COPY 命令将数据加载到表中的特权，请授予或撤消 INSERT 特权。

您的数据需要采用恰当的格式才能加载到 Amazon Redshift 表中。本部分介绍在加载数据前准备和验证数据以及在运行 COPY 语句前对其进行验证的指南。

若要保护文件中的信息，您可以先对数据文件进行加密，然后再将它们上载到 Amazon S3 桶；COPY 将在执行加载时解密数据。您还可以通过向用户提供临时安全凭证来限制对您的加载数据的访问。临时安全凭证可增强安全性，因为它们时效短，过期后无法重复使用。

Amazon Redshift 具有 COPY 内置功能，可以快速加载未压缩的、分隔的数据。但您可以使用 gzip、lzop 或 bzip2 来压缩您的文件，以节约上载文件的时间。

如果 COPY 查询中包含以下关键词，则不支持自动拆分以下格式的未压缩数据：ESCAPE、REMOVEQUOTES 和 FIXEDWIDTH。但是支持 CSV 关键字。

为帮助保护 AWS Cloud 中正在传输的数据，Amazon Redshift 使用硬件加速 SSL 与 Amazon S3 或 Amazon DynamoDB 通信，以执行 COPY、UNLOAD、备份和还原操作。

如果直接从 Amazon DynamoDB 表加载您的表，您可以选择控制自己占用的 Amazon DynamoDB 预配置吞吐量。

作为加载过程的一部分，您可选择让 COPY 分析您的输入数据并对您的表自动应用最佳压缩编码。

**Topics**
+ [凭证和访问权限](loading-data-access-permissions.md)
+ [准备输入数据](t_preparing-input-data.md)
+ [从 Amazon S3 加载数据](t_Loading-data-from-S3.md)
+ [从 Amazon EMR 中加载数据](loading-data-from-emr.md)
+ [从远程主机中加载数据](loading-data-from-remote-hosts.md)
+ [从 Amazon DynamoDB 表中加载数据](t_Loading-data-from-dynamodb.md)
+ [验证是否正确加载了数据](verifying-that-data-loaded-correctly.md)
+ [验证输入数据](t_Validating_input_files.md)
+ [使用自动压缩加载表](c_Loading_tables_auto_compress.md)
+ [针对窄表优化存储](c_load_compression_hidden_cols.md)
+ [加载默认列值](c_loading_default_values.md)
+ [解决数据加载问题](t_Troubleshooting_load_errors.md)

# 凭证和访问权限
<a name="loading-data-access-permissions"></a>

 要使用另一 AWS 资源（如 Amazon S3、Amazon DynamoDB、Amazon EMR 或 Amazon EC2）加载或卸载数据，Amazon Redshift 必须有权访问该资源和有权执行访问数据所需的操作。例如，要从 Amazon S3 加载数据，COPY 必须具有对桶的 LIST 访问权限以及对桶对象的 GET 访问权限。

要获取访问资源的授权，Amazon Redshift 必须经过身份验证。您可以选择基于角色的访问控制或基于密钥的访问控制。本节概述了这两种方法。有关完整的详细信息和示例，请参阅[访问其他 AWS 资源的权限](copy-usage_notes-access-permissions.md)。

## 基于角色的访问控制
<a name="loading-data-access-role-based"></a>

利用基于角色的访问控制，Amazon Redshift 将代表您临时代入 AWS Identity and Access Management（IAM）角色。然后，基于对角色的授权，Amazon Redshift 可以访问所需的 AWS 资源。

我们建议使用基于角色的访问控制，因为除了保护您的 AWS 凭证之外，它还将提供对 AWS 资源和敏感用户数据的更安全、精细的访问控制。

要使用基于角色的访问控制，您必须先使用 Amazon Redshift 服务角色类型创建 IAM 角色，然后将此角色附加到您的数据仓库。此角色至少必须具有 [COPY、UNLOAD 和 CREATE LIBRARY 的 IAM 权限](copy-usage_notes-access-permissions.md#copy-usage_notes-iam-permissions)中列出的权限。有关创建 IAM 角色并将其附加到集群的步骤，请参阅《Amazon Redshift 管理指南》**中的[创建 IAM 角色以允许 Amazon Redshift 集群访问 AWS 服务](https://docs.aws.amazon.com/redshift/latest/mgmt/authorizing-redshift-service.html#authorizing-redshift-service-creating-an-iam-role)。

通过使用 Amazon Redshift 管理控制台、CLI 或 API，您可将角色添加到集群或查看与集群关联的角色。有关更多信息，请参阅《Amazon Redshift 管理指南》**中的[使用 IAM 角色授权 COPY 和 UNLOAD 操作](https://docs.aws.amazon.com/redshift/latest/mgmt/copy-unload-iam-role.html)。

当您创建 IAM 角色时，IAM 将返回该角色的 Amazon 资源名称（ARN）。要使用 IAM 角色运行 COPY 命令，请使用 IAM\$1ROLE 参数或 CREDENTIALS 参数提供角色 ARN。

以下 COPY 命令示例使用角色 `MyRedshiftRole` 的 IAM\$1ROLE 参数进行身份验证。

```
COPY customer FROM 's3://amzn-s3-demo-bucket/mydata' 
IAM_ROLE 'arn:aws:iam::12345678901:role/MyRedshiftRole';
```

AWS 用户必须至少具有[COPY、UNLOAD 和 CREATE LIBRARY 的 IAM 权限](copy-usage_notes-access-permissions.md#copy-usage_notes-iam-permissions)中列出的权限。

## 基于密钥的访问控制
<a name="loading-data-access-key-based"></a>

利用基于密钥的访问控制，您可以为获得授权可访问包含数据的 AWS 资源的用户提供访问密钥 ID 和秘密访问密钥。 

**注意**  
我们强烈建议使用 IAM 角色进行身份验证而不是提供纯文本访问密钥 ID 和秘密访问密钥。如果您选择基于密钥的访问控制，则不要使用 AWS 账户（根）凭证。应始终创建 IAM 用户并提供该用户的访问密钥 ID 和秘密访问密钥。有关创建 IAM 用户的步骤，请参阅[在您的 AWS 账户中创建 IAM 用户](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html)。

# 准备输入数据
<a name="t_preparing-input-data"></a>

如果您的输入数据与将要接收数据的表列不兼容，则 COPY 命令将失败。

请使用以下指南，以帮助确保您的输入数据有效：
+ 您的数据只能包含不超过四个字节长度的 UTF-8 字符。
+ 确认 CHAR 和 VARCHAR 字符串的长度不超过相应列的长度。VARCHAR 字符串以字节（而不是字符）为单位衡量，因此，如果一个字符串有四个中文字符，每个字符占用四个字节，则需要一个 VARCHAR(16) 列。
+ 多字节字符只能用于 VARCHAR 列。确认多字节字符的长度不超过四个字节。
+ 确认 CHAR 列的数据仅包含单字节字符。
+ 不要加入任何特殊字符或语法来表示记录中的最后一个字段。此字段可能为分隔符。
+ 如果数据包括 null 终止符，也称为 NUL (UTF-8 0000) 或二进制零 (0x000)，则可以使用 COPY 命令中的 NULL AS 选项将这些字符作为 NULLS 加载到 CHAR 或 VARCHAR 列中：`null as '\0'` 或 `null as '\000'`。如果您不使用 NULL AS，null 终止符将导致 COPY 失败。
+ 如果您的字符串包含特殊字符（如分隔符和嵌入换行符），请对 [COPY](r_COPY.md) 命令使用 ESCAPE 选项。
+ 确认所有单引号和双引号已正确匹配。
+ 确认浮点字符串采用了标准浮点字符格式（例如 12.123）或指数格式（例如 1.0E4）。
+ 确认所有时间戳和日期字符串遵循了 [DATEFORMAT 和 TIMEFORMAT 字符串示例](r_DATEFORMAT_and_TIMEFORMAT_strings.md) 的说明。默认时间戳格式为 YYYY-MM-DD hh:mm:ss，默认日期格式为 YYYY-MM-DD。
+ 有关各个日期类型的边界和限制的更多信息，请参阅[数据类型](c_Supported_data_types.md)。有关多字节字符错误的信息，请参阅[多字节字符加载错误](multi-byte-character-load-errors.md)

# 从 Amazon S3 加载数据
<a name="t_Loading-data-from-S3"></a>

COPY 命令使用 Amazon Redshift 大规模并行处理 (MPP) 架构从 Amazon S3 桶中的一个或多个文件并行读取和加载数据。在压缩文件的情况下，您可以将数据拆分成多个文件，从而最大程度地利用并行处理。（此规则有例外。[加载数据文件](https://docs.aws.amazon.com/redshift/latest/dg/c_best-practices-use-multiple-files.html)中详细介绍了相关内容。） 您也可以通过在表上设置分配键，从而最大程度地利用并行处理。有关分配键的更多信息，请参阅[用于优化查询的数据分配](t_Distributing_data.md)。

数据将加载到目标表中，一行数据占据表中的一行。数据文件中的字段按从左到右的顺序与表列相匹配。数据文件中的字段可以是固定宽度，也可以用字符分隔；默认分隔符为竖线 (\$1)。默认情况下，将加载所有表列，但您可以选择定义用逗号分隔的列列表。如果 COPY 命令中指定的列列表中不包括某个表列，则该表列将加载为默认值。有关更多信息，请参阅 [加载默认列值](c_loading_default_values.md)。

**Topics**
+ [从压缩和未压缩文件中加载数据](t_splitting-data-files.md)
+ [将文件上传到 Amazon S3 以与 COPY 结合使用](t_uploading-data-to-S3.md)
+ [使用 COPY 命令从 Amazon S3 中加载](t_loading-tables-from-s3.md)

# 从压缩和未压缩文件中加载数据
<a name="t_splitting-data-files"></a>

加载压缩数据时，我们建议您将每个表的数据拆分成多个文件。当您加载未压缩的分隔数据时，COPY 命令使用大规模并行处理 (MPP) 和扫描范围，从 Amazon S3 桶的大型文件中加载数据。

## 从多个压缩文件中加载数据
<a name="t_splitting-data-files-compressed"></a>

如果您有压缩数据，我们建议您将每个表的数据拆分成多个文件。COPY 命令可以从多个文件并行加载数据。您可以通过指定一个通用前缀（对于集合，则为*前缀键*），或通过在清单文件中明确列出文件，从而加载多个文件。

将数据拆分成多个文件，以便文件数是您的集群中的切片数的倍数。这样，Amazon Redshift 就可以在切片之间均匀地拆分数据。每个节点的切片数取决于集群的节点大小。例如，每个 dc2.large 计算节点有两个切片，每个 dc2.8xlarge 计算节点有 16 个切片。有关每个节点大小拥有的切片数的更多信息，请转到《Amazon Redshift 管理指南》**中的[关于集群和节点](https://docs.aws.amazon.com/redshift/latest/mgmt/working-with-clusters.html#rs-about-clusters-and-nodes)。

所有节点均参与并行查询的运行，处理尽可能跨切片均匀分布的数据。如果您的集群有两个 dc2.large 节点，则可以将数据拆分为四个文件或四的倍数个文件。Amazon Redshift 在拆分工作负载时不会考虑文件大小。因此，您需要确保文件大小大致相同，压缩后大小为 1MB 到 1GB。

要使用对象前缀来标识加载文件，为每个文件命名时请加上一个通用前缀。例如，您可以将 `venue.txt` 文件拆分成四个文件，如下所示：

```
venue.txt.1
venue.txt.2
venue.txt.3
venue.txt.4
```

如果您在桶中的一个文件夹中放置多个文件，则可以将该文件夹名称指定为前缀，COPY 会加载该文件夹中的所有文件。如果您使用清单文件明确列出要加载的文件，则这些文件可以位于不同的桶或文件夹中。

有关清单文件的更多信息，请参阅[使用清单指定数据文件](r_COPY_command_examples.md#copy-command-examples-manifest)。

## 从未压缩的分隔文件中加载数据
<a name="t_splitting-data-files-uncompressed"></a>

当您加载未压缩的、分隔的数据时，COPY 命令会使用 Amazon Redshift 中的大规模并行处理 (MPP) 架构。Amazon Redshift 会自动使用并行工作的切片，从 Amazon S3 桶的大型文件中加载多个范围的数据。必须对文件进行分隔才能进行并行加载。例如，竖线分隔。使用 COPY 命令自动并行加载数据也适用于 CSV 文件。您还可以通过在表上设置分配键，从而充分利用并行处理。有关分配键的更多信息，请参阅[用于优化查询的数据分配](t_Distributing_data.md)。

当 COPY 查询包含以下任何关键字时，不支持自动并行加载数据：ESCAPE、REMOVEQUOTES 和 FIXEDWIDTH。

一个或多个文件中的数据将加载到目标表中，一行数据占据表中的一行。数据文件中的字段按从左到右的顺序与表列相匹配。数据文件中的字段可以是固定宽度，也可以用字符分隔；默认分隔符为竖线 (\$1)。默认情况下，将加载所有表列，但您可以选择定义用逗号分隔的列列表。如果 COPY 命令中指定的列列表中不包括某个表列，则该表列将加载为默认值。有关更多信息，请参阅 [加载默认列值](c_loading_default_values.md)。

在数据没有压缩和分隔时，请遵循此一般流程从 Amazon S3 加载数据：

1. 将您的文件上载到 Amazon S3 。

1. 运行 COPY 命令以加载表。

1. 确认数据已正确加载。

有关 COPY 命令的示例，请参阅 [COPY 示例](r_COPY_command_examples.md)。有关加载到 Amazon Redshift 的数据的信息，请查看 [STL\$1LOAD\$1COMMITS](r_STL_LOAD_COMMITS.md) 和 [STL\$1LOAD\$1ERRORS](r_STL_LOAD_ERRORS.md) 系统表。

有关各表中包含的节点以及切片的更多信息，请参阅《Amazon Redshift 管理指南》**中的[关于集群和节点](https://docs.aws.amazon.com/redshift/latest/mgmt/working-with-clusters.html#rs-about-clusters-and-nodes)。

# 将文件上传到 Amazon S3 以与 COPY 结合使用
<a name="t_uploading-data-to-S3"></a>

将文本文件上载到 Amazon S3 时，可以采取以下几种方法：
+ 如果您有压缩文件，我们建议您拆分大文件，以便充分利用 Amazon Redshift 中的并行处理。
+ 另一方面，COPY 会自动拆分大型未压缩文本分隔文件数据，促进并行并有效分发大型文件中的数据。

创建一个用于存储数据文件的 Amazon S3 桶，然后将数据文件上载到该桶。有关创建桶和上载文件的信息，请参阅《Amazon Simple Storage Service 用户指南》**中的[使用 Amazon S3 桶](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingBucket.html)。

**重要**  
必须在与集群相同的 AWS 区域中创建存储数据文件的 Amazon S3 桶，除非您使用 [REGION](copy-parameters-data-source-s3.md#copy-region) 选项指定 Amazon S3 桶所在的区域。

确保将 S3 IP 范围添加到您的允许列表中。要了解有关所需 S3 IP 范围的更多信息，请参阅[网络隔离](https://docs.aws.amazon.com//redshift/latest/mgmt/security-network-isolation.html#network-isolation)。

您可以通过以下方式在特定区域中创建 Amazon S3 桶：在使用 Amazon S3 控制台创建桶时选择该区域，或者在使用 Amazon S3 API 或 CLI 创建桶时指定端点。

数据加载后，确认 Amazon S3 上出现了正确的文件。

**Topics**
+ [管理数据一致性](managing-data-consistency.md)
+ [将加密的数据上载到 Amazon S3](t_uploading-encrypted-data.md)
+ [确认在桶中具有正确的文件](verifying-that-correct-files-are-present.md)

# 管理数据一致性
<a name="managing-data-consistency"></a>

Amazon S3 为所有 AWS 区域中的 Amazon S3 桶上的 COPY、UNLOAD、INSERT（外部表）、CREATE EXTERNAL TABLE AS 和 Amazon Redshift Spectrum 操作提供了强大的先写后读一致性。此外，针对 Amazon S3 Select、Amazon S3 访问控制列表、Amazon S3 对象标签和对象元数据（例如 HEAD 对象）的读取操作具有严格的一致性。有关数据一致性的更多信息，请参阅《Amazon Simple Storage Service 用户指南》**中的 [Amazon S3 数据一致性模型](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Introduction.html#ConsistencyModel)。

# 将加密的数据上载到 Amazon S3
<a name="t_uploading-encrypted-data"></a>

Amazon S3 支持服务器端加密和客户端加密。本主题将讨论服务器端加密和客户端加密的区别，并介绍将客户端加密用于 Amazon Redshift 的步骤。服务器端加密对 Amazon Redshift 是透明的。

## 服务器端加密
<a name="server-side-encryption"></a>

服务器端加密是静态数据加密，即，Amazon S3 在上载数据时对其进行加密，并在您访问时进行解密。当您使用 COPY 命令加载表时，在 Amazon S3 上从服务器端加密或解密的对象的加载方式没有不同。有关服务器端加密的更多信息，请参阅《Amazon Simple Storage Service 用户指南》**中的[使用服务器端加密](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingServerSideEncryption.html)。

## 客户端加密
<a name="client-side-encryption"></a>

在客户端加密中，您的客户端应用程序管理数据的加密、加密密钥和相关的工具。您可以使用客户端加密将数据上载到 Amazon S3 桶，然后使用带有 ENCRYPTED 选项的 COPY 命令和私有加密密钥加载数据，以实现更强的安全性。

您使用信封加密来加密您的数据。借助*信封加密*，您的应用程序可专门处理所有加密。您的私有加密密钥和未加密的数据从来不会发送到 AWS，因此请您务必妥善管理好您的加密密钥。如果您丢失了加密密钥，您将无法解密数据，而且，您无法从 AWS 找回您的加密密钥。信封加密结合了快速对称加密的性能，同时保持了使用非对称密钥进行密钥管理所获得的更强的安全性。Amazon S3 加密客户端生成一次性对称密钥（信封对称密钥）来加密数据，然后由您的根密钥对其进行加密并与您的数据一起存储在 Amazon S3 中。在加载过程中，当 Amazon Redshift 访问您的数据时，将检索加密的对称密钥并使用您的实际密钥对其进行解密，然后解密数据。

要在 Amazon Redshift 中使用 Amazon S3 客户端加密数据，请按照《Amazon Simple Storage Service 用户指南》**中的[使用客户端加密保护数据](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingClientSideEncryption.html)所列的步骤操作，并满足您使用的其它要求：
+ **对称加密 –** AWS SDK for Java `AmazonS3EncryptionClient` 类使用信封加密，如前所述，信封加密是基于对称密钥加密的。使用此类可创建要上载客户端加密数据的 Amazon S3 客户端。
+ **256 位 AES 根对称密钥 –** 根密钥将对信封密钥进行加密。您将根密钥传递给 `AmazonS3EncryptionClient` 类的实例。保存此密钥，因为您将需要用它来将数据复制到 Amazon Redshift 中。
+ **存储加密信封密钥的对象元数据 –** 预设情况下，Amazon S3 将信封密钥存储为 `AmazonS3EncryptionClient` 类的对象元数据。存储为对象元数据的加密信封密钥将在加密过程中使用。

**注意**  
首次使用加密 API 时，如果您收到密码加密错误消息，则您的 JDK 版本可能带有一个 Java Cryptography Extension (JCE) 区域策略文件，该文件将加密和解密转换的最大密钥长度限制为 128 位。有关解决此问题的信息，请转到《Amazon Simple Storage Service 用户指南》**中的[使用 AWS SDK for Java 指定客户端加密](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingClientSideEncryptionUpload.html)。

有关使用 COPY 命令将客户端加密的文件加载到 Amazon Redshift 表中的信息，请参阅[从 Amazon S3 中加载加密的数据文件](c_loading-encrypted-files.md)。

## 示例：上传客户端加密数据
<a name="client-side-encryption-example"></a>

有关如何使用 AWS SDK for Java 上载客户端加密数据的示例，请转到《Amazon Simple Storage Service 用户指南》**中的[使用客户端加密保护数据](https://docs.aws.amazon.com/AmazonS3/latest/userguide/encrypt-client-side-symmetric-master-key.html)。

第二个选项展示了您若要在 Amazon Redshift 中加载数据而必须在客户端加密期间做出的选择。具体来讲，该示例展示了使用对象元数据来存储加密信封密钥和 256 位 AES 根对称密钥的使用。

本示例代码使用 AWS SDK for Java 创建 256 位 AES 对称根密钥并将它保存到文件。然后，本示例使用首先加密客户端上样本数据的 S3 加密客户端将一个对象上传到 Amazon S3。示例还将下载该对象，并验证数据是否相同。

# 确认在桶中具有正确的文件
<a name="verifying-that-correct-files-are-present"></a>

将文件上载到 Amazon S3 桶之后，我们建议您列出桶的内容，以便确认所有正确文件均已存在并且不存在不需要的文件。例如，如果桶 `amzn-s3-demo-bucket` 存储了一个名为 `venue.txt.back` 的文件，则该文件可能会被以下命令意外加载：

```
COPY venue FROM 's3://amzn-s3-demo-bucket/venue' … ;
```

如果您要控制具体加载哪些文件，则可使用清单文件明确列出所需的数据文件。有关使用清单文件的更多信息，请参阅 COPY 命令的 [copy_from_s3_manifest_file](copy-parameters-data-source-s3.md#copy-manifest-file) 选项和 COPY 示例中的[使用清单指定数据文件](r_COPY_command_examples.md#copy-command-examples-manifest)。

有关列出桶内容的更多信息，请参阅《Amazon S3 开发人员指南》**中的[列出对象键](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ListingKeysUsingAPIs.html)。

# 使用 COPY 命令从 Amazon S3 中加载
<a name="t_loading-tables-from-s3"></a>

使用 [COPY](r_COPY.md) 命令从 Amazon S3 上的数据文件并行加载表。您可以使用 Amazon S3 对象前缀或清单文件指定要加载的文件。

使用前缀指定要加载的文件的语法如下所示：

```
COPY <table_name> FROM 's3://<bucket_name>/<object_prefix>'
authorization;
```

 清单文件是 JSON 格式的文件，该文件会列出要加载的数据文件。使用清单文件指定要加载的文件的语法如下所示：

```
COPY <table_name> FROM 's3://<bucket_name>/<manifest_file>'
authorization
MANIFEST;
```

数据库中必须已存在要加载的表。有关创建表的信息，请参阅 SQL 参考中的[CREATE TABLE](r_CREATE_TABLE_NEW.md)。

*authorization* 的值提供 Amazon Redshift 访问 Amazon S3 对象所需的 AWS 授权。有关所需权限的信息，请参阅[COPY、UNLOAD 和 CREATE LIBRARY 的 IAM 权限](copy-usage_notes-access-permissions.md#copy-usage_notes-iam-permissions)。进行身份验证的首选方法是为具有必要权限的 IAM 角色指定 IAM\$1ROLE 参数并提供 Amazon 资源名称（ARN）。有关更多信息，请参阅 [基于角色的访问控制](copy-usage_notes-access-permissions.md#copy-usage_notes-access-role-based)。

要使用 IAM\$1ROLE 参数进行身份验证，请替换 *<aws-account-id>* 和 *<role-name>*，如以下语法中所示。

```
IAM_ROLE 'arn:aws:iam::<aws-account-id>:role/<role-name>'
```

以下示例使用 IAM 角色进行身份验证。

```
COPY customer 
FROM 's3://amzn-s3-demo-bucket/mydata' 
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole';
```

有关其他授权选项的更多信息，请参阅[授权参数](copy-parameters-authorization.md)。

如果您要验证您的数据而不实际加载表，请对 [COPY](r_COPY.md) 命令使用 NOLOAD 选项。

以下示例展示了一个名为 `venue.txt` 的文件中用竖线分隔的数据的前几行。

```
1|Toyota Park|Bridgeview|IL|0
2|Columbus Crew Stadium|Columbus|OH|0
3|RFK Stadium|Washington|DC|0
```

在将文件上载到 Amazon S3 之前，将文件拆分成多个文件，以便 COPY 命令使用并行处理来加载它。文件数应为您的集群中的切片数的倍数。拆分您的加载数据文件，使文件大小大约相等，压缩后的文件大小介于 1 MB 和 1 GB 之间。有关更多信息，请参阅 [从压缩和未压缩文件中加载数据](t_splitting-data-files.md)。

例如，`venue.txt` 文件可拆分成四个文件，如下所示：

```
venue.txt.1
venue.txt.2
venue.txt.3
venue.txt.4
```

以下 COPY 命令将使用 Amazon S3 桶 `amzn-s3-demo-bucket` 中带有前缀“venue”的数据文件中用竖线分隔的数据加载 VENUE 表。

**注意**  
Amazon S3 桶 `amzn-s3-demo-bucket` 在以下示例中不存在。有关使用现有 Amazon S3 桶中的实际数据的示例 COPY 命令，请参阅[加载示例数据](https://docs.aws.amazon.com/redshift/latest/gsg/cm-dev-t-load-sample-data.html)。

```
COPY venue FROM 's3://amzn-s3-demo-bucket/venue'
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole'
DELIMITER '|';
```

如果带有键前缀“venue”的 Amazon S3 对象不存在，则加载将失败。

**Topics**
+ [使用清单指定数据文件](loading-data-files-using-manifest.md)
+ [从 Amazon S3 中加载压缩的数据文件](t_loading-gzip-compressed-data-files-from-S3.md)
+ [从 Amazon S3 中加载固定宽度的数据](t_loading_fixed_width_data.md)
+ [从 Amazon S3 中加载多字节数据](t_loading_unicode_data.md)
+ [从 Amazon S3 中加载加密的数据文件](c_loading-encrypted-files.md)

# 使用清单指定数据文件
<a name="loading-data-files-using-manifest"></a>

您可以对数据加载使用清单以确保 COPY 命令加载所有需要的文件，且仅加载需要的文件。您可以使用清单来加载不同桶或文件中的未共享相同前缀的文件。您可以提供明确列出要加载的文件的 JSON 格式的文本文件的名称，而不必提供 COPY 命令的对象路径。清单中的 URL 必须指定桶名称和文件的完整对象路径，而不仅仅是前缀。

有关清单文件的更多信息，请参阅 COPY 示例[使用清单指定数据文件](r_COPY_command_examples.md#copy-command-examples-manifest)。

以下示例展示了用于加载不同桶中的文件名以日期戳开头的文件的 JSON。

```
{
  "entries": [
    {"url":"s3://amzn-s3-demo-bucket1/2013-10-04-custdata", "mandatory":true},
    {"url":"s3://amzn-s3-demo-bucket1/2013-10-05-custdata", "mandatory":true},
    {"url":"s3://amzn-s3-demo-bucket2/2013-10-04-custdata", "mandatory":true},
    {"url":"s3://amzn-s3-demo-bucket2/2013-10-05-custdata", "mandatory":true}
  ]
}
```

可选的 `mandatory` 标志指定 COPY 是否应在找不到文件时返回错误。`mandatory` 的默认值为 `false`。如果未找到任何文件，则无论 mandatory 设置如何，COPY 都将终止。

以下示例将使用前一个示例中名为 `cust.manifest` 的清单来运行 COPY 命令。

```
COPY customer
FROM 's3://amzn-s3-demo-bucket/cust.manifest' 
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole'
MANIFEST;
```

## 使用 UNLOAD 创建的清单
<a name="loading-data-files-using-unload-manifest"></a>

由 [UNLOAD](r_UNLOAD.md) 操作使用 MANIFEST 参数创建的清单可能具有 COPY 操作不需要的键。例如，以下 `UNLOAD` 清单包含一个 `meta` 键，该键是 Amazon Redshift Spectrum 外部表所必需的，并用于加载 `ORC` 或 `Parquet` 文件格式的数据文件。`meta` 键包含具有文件实际大小值（以字节为单位）的 `content_length` 键。COPY 操作只需要 `url` 密钥和可选 `mandatory` 密钥。

```
{
  "entries": [
    {"url":"s3://amzn-s3-demo-bucket/unload/manifest_0000_part_00", "meta": { "content_length": 5956875 }},
    {"url":"s3://amzn-s3-demo-bucket/unload/unload/manifest_0001_part_00", "meta": { "content_length": 5997091 }}
 ]
}
```

有关清单文件的更多信息，请参阅[使用清单指定数据文件](r_COPY_command_examples.md#copy-command-examples-manifest)。

# 从 Amazon S3 中加载压缩的数据文件
<a name="t_loading-gzip-compressed-data-files-from-S3"></a>

要加载使用 gzip、lzop 或 bzip2 压缩的数据文件，请包括相应的选项：GZIP、LZOP 或 BZIP2。

例如，以下命令将从使用 lzop 压缩的文件加载。

```
COPY customer FROM 's3://amzn-s3-demo-bucket/customer.lzo' 
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole'
DELIMITER '|' LZOP;
```

**注意**  
如果您使用 lzop 压缩来压缩数据文件并使用 *--filter* 选项，则 COPY 命令不支持该文件。

# 从 Amazon S3 中加载固定宽度的数据
<a name="t_loading_fixed_width_data"></a>

固定宽度的数据文件的每个数据列具有统一的长度。固定宽度的数据文件中的每个字段的长度和位置完全相同。对于固定宽度的数据文件中的字符数据（CHAR 和 VARCHAR），您必须包括前导空格或尾随空格作为占位符，以便保持宽度统一。对于整数，您必须使用前导零作为占位符。固定宽度的数据文件没有用于分隔列的分隔符。

要将固定宽度的数据文件加载到现有表，请在 COPY 命令中使用 FIXEDWIDTH 参数。您的表说明必须与 fixedwidth\$1spec 的值相匹配才能正确加载数据。

要将固定宽度的数据从文件加载到表，请发出以下命令：

```
COPY table_name FROM 's3://amzn-s3-demo-bucket/prefix' 
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole' 
FIXEDWIDTH 'fixedwidth_spec';
```

*fixedwidth\$1spec* 参数是一个字符串，其中包含每个列的标识符和每个列的宽度（用冒号分隔）。**column:width** 对由逗号分隔。标识符可以是您选择的任意形式：数字、字母或二者的组合。标识符与表本身无关，因此说明包含列的顺序必须与表中的列顺序相同。

以下两个示例演示了相同说明，第一个使用数字标识符，第二个使用字符串标识符：

```
'0:3,1:25,2:12,3:2,4:6'
```

```
'venueid:3,venuename:25,venuecity:12,venuestate:2,venueseats:6'
```

以下示例演示了固定宽度的样本数据，可使用前面的说明将这些数据加载到 VENUE 表中：

```
1  Toyota Park               Bridgeview  IL0
2  Columbus Crew Stadium     Columbus    OH0
3  RFK Stadium               Washington  DC0
4  CommunityAmerica Ballpark Kansas City KS0
5  Gillette Stadium          Foxborough  MA68756
```

以下 COPY 命令会将此数据集加载到 VENUE 表中：

```
COPY venue
FROM 's3://amzn-s3-demo-bucket/data/venue_fw.txt' 
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole' 
FIXEDWIDTH 'venueid:3,venuename:25,venuecity:12,venuestate:2,venueseats:6';
```

# 从 Amazon S3 中加载多字节数据
<a name="t_loading_unicode_data"></a>

如果您的数据包含非 ASCII 多字节字符（例如中文或西里尔语字符），则必须将该数据加载到 VARCHAR 列。VARCHAR 数据类型支持四字节的 UTF-8 字符，而 CHAR 数据类型仅接受单字节的 ASCII 字符。您不能将五字节或更长的字符加载到 Amazon Redshift 表中。有关 CHAR 和 VARCHAR 的更多信息，请参阅[数据类型](c_Supported_data_types.md)。

要检查输入文件使用哪种编码，请使用 Linux * `file` * 命令：

```
$ file ordersdata.txt
ordersdata.txt: ASCII English text
$ file uni_ordersdata.dat
uni_ordersdata.dat: UTF-8 Unicode text
```

# 从 Amazon S3 中加载加密的数据文件
<a name="c_loading-encrypted-files"></a>

您可以使用 COPY 命令加载使用服务器端加密、客户端加密或两种加密方式上载到 Amazon S3 的数据文件。

COPY 命令支持以下 Amazon S3 加密方式：
+ 使用 Amazon S3 托管密钥进行服务器端加密 (SSE-S3)
+ 使用 AWS KMS keys 的服务器端加密 (SSE-KMS)
+ 使用客户端对称根密钥进行客户端加密

COPY 命令不支持以下 Amazon S3 加密方式：
+ 使用客户提供的密钥进行服务器端加密 (SSE-C)
+ 使用 AWS KMS key 进行客户端加密
+ 使用客户提供的对称根密钥进行客户端加密

有关 Amazon S3 加密的更多信息，请参阅《Amazon Simple Storage Service 用户指南》中的[使用服务器端加密保护数据](https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html)和[使用客户端加密保护数据](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingClientSideEncryption.html)。

[UNLOAD](r_UNLOAD.md) 命令使用 SSE-S3 自动加密文件。您还可以使用 SSE-KMS 或客户托管式对称密钥进行客户端加密的方法进行卸载。有关更多信息，请参阅 [卸载加密的数据文件](t_unloading_encrypted_files.md)。

COPY 命令会自动识别并加载使用 SSE-S3 和 SSE-KMS 进行加密的文件。您可以指定 ENCRYPTED 选项并提供密钥值，加载使用客户端对称根密钥加密的文件。有关更多信息，请参阅 [将加密的数据上载到 Amazon S3](t_uploading-encrypted-data.md)。

要加载客户端加密数据文件，请使用 MASTER\$1SYMMETRIC\$1KEY 参数提供根密钥值，并包括 ENCRYPTED 选项。

```
COPY customer FROM 's3://amzn-s3-demo-bucket/encrypted/customer' 
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole'
MASTER_SYMMETRIC_KEY '<root_key>' 
ENCRYPTED
DELIMITER '|';
```

要加载 gzip、lzop 或 bzip2 压缩的加密数据文件，请随根密钥值和 ENCRYPTED 选项包括 GZIP、LZOP 或 BZIP2 选项。

```
COPY customer FROM 's3://amzn-s3-demo-bucket/encrypted/customer' 
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole'
MASTER_SYMMETRIC_KEY '<root_key>'
ENCRYPTED 
DELIMITER '|' 
GZIP;
```

# 从 Amazon EMR 中加载数据
<a name="loading-data-from-emr"></a>

您可以使用 COPY 命令从一个具有如下配置的 Amazon EMR 集群并行加载数据：将文本文件作为固定宽度文件、字符分隔文件、CSV 文件或 JSON 格式文件写入到集群的 Hadoop Distributed File System (HDFS)。

## 从 Amazon EMR 中加载数据的过程
<a name="load-from-emr-process"></a>

本节演练从 Amazon EMR 集群加载数据的过程。以下各节提供您必须完成每个步骤的详细信息。
+ **[步骤 1：配置 IAM 权限](#load-from-emr-steps-configure-iam)**

  用户必须拥有必要的权限才能创建 Amazon EMR 集群和运行 Amazon Redshift COPY 命令。
+ **[步骤 2：创建 Amazon EMR 集群](#load-from-emr-steps-create-cluster)**

  将集群配置为将文本文件输出到 Hadoop Distributed File System (HDFS)。您需要 Amazon EMR 集群 ID 和集群的主节点公有 DNS（托管集群的 Amazon EC2 实例的端点）。
+ **[步骤 3：检索 Amazon Redshift 集群公有密钥和集群节点 IP 地址](#load-from-emr-steps-retrieve-key-and-ips)**

  公有密钥使 Amazon Redshift 集群节点能够建立与主机的 SSH 连接。您将使用每个集群节点的 IP 地址来配置主机安全组，从而允许使用这些 IP 地址从 Amazon Redshift 集群访问。
+ **[步骤 4：将 Amazon Redshift 集群公有密钥添加到每个 Amazon EC2 主机的授权密钥文件](#load-from-emr-steps-add-key-to-host)** 

  您将 Amazon Redshift 集群公有密钥添加到主机的授权密钥文件，以便让主机识别 Amazon Redshift 集群并接受 SSH 连接。
+ **[步骤 5：将主机配置为接受 Amazon Redshift 集群的所有 IP 地址](#load-from-emr-steps-configure-security-groups)** 

  修改 Amazon EMR 实例的安全组，以添加接受 Amazon Redshift IP 地址的输入规则。
+ **[步骤 6：运行 COPY 命令以加载数据](#load-from-emr-steps-run-copy)**

  从 Amazon Redshift 数据库运行 COPY 命令，以便将数据加载到 Amazon Redshift 表中。

## 步骤 1：配置 IAM 权限
<a name="load-from-emr-steps-configure-iam"></a>

用户必须拥有必要的权限才能创建 Amazon EMR 集群和运行 Amazon Redshift COPY 命令。

**配置 IAM 权限**

1. 为将要创建 Amazon EMR 集群的用户添加以下权限。

   ```
   ec2:DescribeSecurityGroups
   ec2:RevokeSecurityGroupIngress
   ec2:AuthorizeSecurityGroupIngress
   redshift:DescribeClusters
   ```

1. 为将要运行 COPY 命令的 IAM 角色或用户添加以下权限。

   ```
   elasticmapreduce:ListInstances
   ```

1. 向 Amazon EMR 集群的 IAM 角色添加以下权限。

   ```
   redshift:DescribeClusters
   ```

## 步骤 2：创建 Amazon EMR 集群
<a name="load-from-emr-steps-create-cluster"></a>

COPY 命令从 Amazon EMR Hadoop Distributed File System (HDFS) 上的文件加载数据。当您创建 Amazon EMR 集群时，请将集群配置为将数据文件输出到集群的 HDFS。

**要创建 Amazon EMR 集群**

1. 在与 Amazon Redshift 集群相同的 AWS 区域中创建 Amazon EMR 集群。

   如果 Amazon Redshift 集群在 VPC 中，则 Amazon EMR 集群必须在同一 VPC 组中。如果 Amazon Redshift 集群使用 EC2-Classic 模式（即，它不在 VPC 中），则 Amazon EMR 集群必须也使用 EC2-Classic 模式。有关更多信息，请参阅《Amazon Redshift 管理指南》**中的[管理 Virtual Private Cloud (VPC) 中的集群](https://docs.aws.amazon.com/redshift/latest/mgmt/managing-clusters-vpc.html)。

1. 将集群配置为将数据文件输出到集群的 HDFS。HDFS 文件名不能包括星号 (\$1) 或问号 (?)。
**重要**  
文件名不能包括星号 (\$1) 或问号 (?)。

1. 在 Amazon EMR 集群配置中，将**自动终止**选项指定为**否**，以便集群在 COPY 命令运行时保持可用。
**重要**  
如果在 COPY 完成前更改或删除了任何数据文件，则您可能会遇到意外结果，或者 COPY 操作可能失败。

1. 请记下集群 ID 和主节点公有 DNS（托管集群的 Amazon EC2 实例的端点）。您将在后面的步骤中用到这些信息。

## 步骤 3：检索 Amazon Redshift 集群公有密钥和集群节点 IP 地址
<a name="load-from-emr-steps-retrieve-key-and-ips"></a>

您将使用每个集群节点的 IP 地址来配置主机安全组，从而允许使用这些 IP 地址从 Amazon Redshift 集群访问。

**要使用控制台为您的集群检索 Amazon Redshift 集群公有密钥和集群节点 IP 地址**

1. 访问 Amazon Redshift 管理控制台。

1. 在导航窗格中选择**集群**链接。

1. 从列表中选择您的集群。

1. 找到 **SSH 数据摄取**组。

   记下 **Cluster Public Key** 和 **Node IP addresses** 中的值。您将在后面的步骤中用到它们。  
![\[SSH 数据摄取组中的屏幕截图，显示了集群公钥和节点 IP 地址。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/copy-from-ssh-console-2.png)

   您将使用步骤 3 中的私有 IP 地址将 Amazon EC2 主机配置为接受来自 Amazon Redshift 的连接。

若要使用 Amazon Redshift CLI 检索您的集群的集群公有密钥和集群节点 IP 地址，请运行 describe-clusters 命令。例如：

```
aws redshift describe-clusters --cluster-identifier <cluster-identifier> 
```

响应将包括 ClusterPublicKey 值和私有 IP 地址及公有 IP 地址的列表，类似于以下内容：

```
{
    "Clusters": [
        {
            "VpcSecurityGroups": [], 
            "ClusterStatus": "available", 
            "ClusterNodes": [
                {
                    "PrivateIPAddress": "10.nnn.nnn.nnn", 
                    "NodeRole": "LEADER", 
                    "PublicIPAddress": "10.nnn.nnn.nnn"
                }, 
                {
                    "PrivateIPAddress": "10.nnn.nnn.nnn", 
                    "NodeRole": "COMPUTE-0", 
                    "PublicIPAddress": "10.nnn.nnn.nnn"
                }, 
                {
                    "PrivateIPAddress": "10.nnn.nnn.nnn", 
                    "NodeRole": "COMPUTE-1", 
                    "PublicIPAddress": "10.nnn.nnn.nnn"
                }
            ], 
            "AutomatedSnapshotRetentionPeriod": 1, 
            "PreferredMaintenanceWindow": "wed:05:30-wed:06:00", 
            "AvailabilityZone": "us-east-1a", 
            "NodeType": "dc2.large", 
            "ClusterPublicKey": "ssh-rsa AAAABexamplepublickey...Y3TAl Amazon-Redshift", 
             ...
             ...
}
```

要使用 Amazon Redshift API 检索您的集群的集群公有密钥和集群节点 IP 地址，请使用 `DescribeClusters` 操作。有关更多信息，请参阅《Amazon Redshift CLI 指南》**中的 [describe-clusters](https://docs.aws.amazon.com/cli/latest/reference/redshift/describe-clusters.html) 或《Amazon Redshift API 指南》中的 [DescribeClusters](https://docs.aws.amazon.com/redshift/latest/APIReference/API_DescribeClusters.html)。

## 步骤 4：将 Amazon Redshift 集群公有密钥添加到每个 Amazon EC2 主机的授权密钥文件
<a name="load-from-emr-steps-add-key-to-host"></a>

您将所有 Amazon EMR 集群节点的集群公有密钥添加到每个主机的授权密钥文件，以便主机识别 Amazon Redshift 并接受 SSH 连接。

**要将 Amazon Redshift 集群公有密钥添加到主机的授权密钥文件**

1. 使用 SSH 连接访问主机。

   有关使用 SSH 连接到实例的信息，请参阅《Amazon EC2 用户指南》**中的[连接到您的实例](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-connect-to-instance-linux.html)。

1. 从控制台或从 CLI 响应文本复制 Amazon Redshift 公有密钥。

1. 将公有密钥的内容复制并粘贴到主机上的 `/home/<ssh_username>/.ssh/authorized_keys` 文件中。请包括完整字符串（包含前缀“`ssh-rsa`”和后缀“`Amazon-Redshift`”）。例如：

   ```
   ssh-rsa AAAACTP3isxgGzVWoIWpbVvRCOzYdVifMrh… uA70BnMHCaMiRdmvsDOedZDOedZ Amazon-Redshift
   ```

## 步骤 5：将主机配置为接受 Amazon Redshift 集群的所有 IP 地址
<a name="load-from-emr-steps-configure-security-groups"></a>

 若要允许到主机实例的入站流量，请编辑安全组并为每个 Amazon Redshift 集群节点添加一个入站规则。对于 **Type**，请选择在端口 22 上使用 TCP 协议的 SSH。对于**源**，请输入您在[步骤 3：检索 Amazon Redshift 集群公有密钥和集群节点 IP 地址](#load-from-emr-steps-retrieve-key-and-ips)中检索的 Amazon Redshift 集群节点私有 IP 地址。有关添加规则到 Amazon EC2 安全组的信息，请参阅《Amazon EC2 用户指南》**中的[为您的实例授权入站流量](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/authorizing-access-to-an-instance.html)。

## 步骤 6：运行 COPY 命令以加载数据
<a name="load-from-emr-steps-run-copy"></a>

运行 [COPY](r_COPY.md) 命令以连接到 Amazon EMR 集群并将数据加载到 Amazon Redshift 表中。Amazon EMR 集群必须继续运行，直到 COPY 命令完成。例如，不要将集群配置为自动终止。

**重要**  
如果在 COPY 完成前更改或删除了任何数据文件，则您可能会遇到意外结果，或者 COPY 操作可能失败。

在 COPY 命令中，指定 Amazon EMR 集群 ID 和 HDFS 文件路径及文件名。

```
COPY sales
FROM 'emr://myemrclusterid/myoutput/part*' CREDENTIALS 
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole';
```

您可以使用通配符星号 (`*`) 和问号 (`?`) 作为文件名参数的一部分。例如，`part*` 加载文件 `part-0000`、`part-0001`，等等。如果您仅指定一个文件夹名称，则 COPY 将尝试加载该文件夹中的所有文件。

**重要**  
如果您使用通配符或仅使用文件夹名称，请确认不会加载不需要的文件，否则 COPY 命令将失败。例如，某些流程可能会将日志文件写入到输出文件夹。

# 从远程主机中加载数据
<a name="loading-data-from-remote-hosts"></a>

您可使用 COPY 命令从一个或多个远程主机并行加载数据，例如 Amazon EC2 实例或其他计算机。COPY 将连接到使用 SSH 的远程主机并在远程主机上运行命令以生成文本输出。

远程主机可以是 Amazon EC2 Linux 实例或配置为接受 SSH 连接的另一台 Unix 或 Linux 计算机。本指南假定您的远程主机是 Amazon EC2 实例。如果过程与其他计算机不同，指南中将会指出差别。

Amazon Redshift 可连接到多台主机，并可以打开到每台主机的多个 SSH 连接。Amazon Redshift 会通过每个连接发送一个唯一命令来生成到主机标准输出的文本输出，然后 Amazon Redshift 会像读取文本文件一样读取它。

## 开始前的准备工作
<a name="load-from-host-before-you-begin"></a>

在开始之前，您应做好以下准备：
+ 您可使用 SSH 连接的一个或多个主机（如 Amazon EC2 实例）。
+ 主机上的数据来源。

  您将提供一些命令，Amazon Redshift 集群将在主机上运行这些命令以生成文本输出。在集群连接到主机后，COPY 命令将运行这些命令，从主机的标准输出中读取文本，并将数据并行加载到 Amazon Redshift 表中。文本输出必须采用 COPY 命令可提取的形式。有关更多信息，请参阅[准备输入数据](t_preparing-input-data.md)。
+ 从您的计算机访问主机的权限。

  对于 Amazon EC2 实例，您将使用 SSH 连接来访问主机。您必须访问主机以将 Amazon Redshift 集群的公有密钥添加到主机的授权密钥文件。
+ 正在运行的 Amazon Redshift 集群。

  有关如何启动集群的信息，请参阅 [Amazon Redshift 入门指南](https://docs.aws.amazon.com/redshift/latest/gsg/)。

## 加载数据的过程
<a name="load-from-host-process"></a>

本节指导您完成从远程主机加载数据的过程。以下各节提供每个步骤中必须完成的操作的详细信息。
+ **[步骤 1：检索集群公有密钥和集群节点 IP 地址](#load-from-host-steps-retrieve-key-and-ips)**

  公有密钥使 Amazon Redshift 集群节点能够建立与远程主机的 SSH 连接。您将使用每个集群节点的 IP 地址来配置主机安全组或防火墙，从而允许使用这些 IP 地址从 Amazon Redshift 集群进行访问。
+ **[步骤 2：将 Amazon Redshift 集群公有密钥添加到主机的授权密钥文件](#load-from-host-steps-add-key-to-host)**

  您将 Amazon Redshift 集群公有密钥添加到主机的授权密钥文件，以便让主机识别 Amazon Redshift 集群并接受 SSH 连接。
+ **[步骤 3：将主机配置为接受 Amazon Redshift 集群的所有 IP 地址](#load-from-host-steps-configure-security-groups)** 

  对于 Amazon EC2，修改该实例的安全组，以添加接受 Amazon Redshift IP 地址的输入规则。对于其他主机，请修改防火墙，以便 Amazon Redshift 节点能够建立与远程主机的 SSH 连接。
+ **[步骤 4：获取主机的公有密钥](#load-from-host-steps-get-the-host-key)**

  您可以选择指定 Amazon Redshift 应使用公有密钥来标识主机。您必须找到公有密钥并将文本复制到您的清单文件中。
+ **[步骤 5：创建清单文件](#load-from-host-steps-create-manifest)** 

  清单是一个 JSON 格式的文本文件，其中包含 Amazon Redshift 连接到主机并提取数据所需的详细信息。
+ **[步骤 6：将清单文件上载到 Amazon S3 桶](#load-from-host-steps-upload-manifest)** 

  Amazon Redshift 将读取清单文件并使用该信息连接到远程主机。如果 Amazon S3 桶不在您的 Amazon Redshift 集群所在的区域内，则必须使用 [REGION](copy-parameters-data-source-s3.md#copy-region) 选项指定数据所在的区域。
+ **[步骤 7：运行 COPY 命令以加载数据](#load-from-host-steps-run-copy)**

  从 Amazon Redshift 数据库运行 COPY 命令，以便将数据加载到 Amazon Redshift 表中。

## 步骤 1：检索集群公有密钥和集群节点 IP 地址
<a name="load-from-host-steps-retrieve-key-and-ips"></a>

您将使用每个集群节点的 IP 地址来配置主机安全组，从而允许使用这些 IP 地址从 Amazon Redshift 集群访问。

**使用控制台为您的集群检索集群公有密钥和集群节点 IP 地址**

1. 访问 Amazon Redshift 管理控制台。

1. 在导航窗格中选择**集群**链接。

1. 从列表中选择您的集群。

1. 找到 **SSH 数据摄取**组。

   记下 **Cluster Public Key** 和 **Node IP addresses** 中的值。您将在后面的步骤中用到它们。  
![\[SSH 数据摄取组中的屏幕截图，显示了集群公钥和节点 IP 地址。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/copy-from-ssh-console-2.png)

   您将使用步骤 3 中的 IP 地址将主机配置为接受来自 Amazon Redshift 的连接。根据您连接到的主机的类型以及该主机是否在 VPC 中，您将使用公有 IP 地址或私有 IP 地址。

若要使用 Amazon Redshift CLI 检索您的集群的集群公有密钥和集群节点 IP 地址，请运行 describe-clusters 命令。

例如：

```
aws redshift describe-clusters --cluster-identifier <cluster-identifier> 
```

 响应将包含 ClusterPublicKey 以及私有和公有 IP 地址的列表，类似于以下内容：

```
{
    "Clusters": [
        {
            "VpcSecurityGroups": [], 
            "ClusterStatus": "available", 
            "ClusterNodes": [
                {
                    "PrivateIPAddress": "10.nnn.nnn.nnn", 
                    "NodeRole": "LEADER", 
                    "PublicIPAddress": "10.nnn.nnn.nnn"
                }, 
                {
                    "PrivateIPAddress": "10.nnn.nnn.nnn", 
                    "NodeRole": "COMPUTE-0", 
                    "PublicIPAddress": "10.nnn.nnn.nnn"
                }, 
                {
                    "PrivateIPAddress": "10.nnn.nnn.nnn", 
                    "NodeRole": "COMPUTE-1", 
                    "PublicIPAddress": "10.nnn.nnn.nnn"
                }
            ], 
            "AutomatedSnapshotRetentionPeriod": 1, 
            "PreferredMaintenanceWindow": "wed:05:30-wed:06:00", 
            "AvailabilityZone": "us-east-1a", 
            "NodeType": "dc2.large", 
            "ClusterPublicKey": "ssh-rsa AAAABexamplepublickey...Y3TAl Amazon-Redshift", 
             ...
             ...
}
```

若要使用 Amazon Redshift API 检索您的集群的集群公有密钥和集群节点 IP 地址，请使用 DescribeClusters 操作。有关更多信息，请参阅《Amazon Redshift CLI 指南》**中的 [describe-clusters](https://docs.aws.amazon.com/cli/latest/reference/redshift/describe-clusters.html) 或《Amazon Redshift API 指南》中的 [DescribeClusters](https://docs.aws.amazon.com/redshift/latest/APIReference/API_DescribeClusters.html)。

## 步骤 2：将 Amazon Redshift 集群公有密钥添加到主机的授权密钥文件
<a name="load-from-host-steps-add-key-to-host"></a>

您将集群公有密钥添加到每个主机的授权密钥文件，以便让主机识别 Amazon Redshift 并接受 SSH 连接。

**要将 Amazon Redshift 集群公有密钥添加到主机的授权密钥文件**

1. 使用 SSH 连接访问主机。

   有关使用 SSH 连接到实例的信息，请参阅《Amazon EC2 用户指南》**中的[连接到您的实例](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-connect-to-instance-linux.html)。

1. 从控制台或从 CLI 响应文本复制 Amazon Redshift 公有密钥。

1. 将公有密钥的内容复制并粘贴到远程主机上的 `/home/<ssh_username>/.ssh/authorized_keys` 文件中。`<ssh_username>` 必须与清单文件中的“username”字段的值匹配。请包括完整字符串（包含前缀“`ssh-rsa`”和后缀“`Amazon-Redshift`”）。例如：

   ```
   ssh-rsa AAAACTP3isxgGzVWoIWpbVvRCOzYdVifMrh… uA70BnMHCaMiRdmvsDOedZDOedZ Amazon-Redshift
   ```

## 步骤 3：将主机配置为接受 Amazon Redshift 集群的所有 IP 地址
<a name="load-from-host-steps-configure-security-groups"></a>

 如果要使用 Amazon EC2 实例或 Amazon EMR 集群，请向主机的安全组添加入站规则以允许来自每个 Amazon Redshift 集群节点的流量。对于 **Type**，请选择在端口 22 上使用 TCP 协议的 SSH。对于**源**，请输入您在[步骤 1：检索集群公有密钥和集群节点 IP 地址](#load-from-host-steps-retrieve-key-and-ips)中检索的 Amazon Redshift 集群节点 IP 地址。有关添加规则到 Amazon EC2 安全组的信息，请参阅《Amazon EC2 用户指南》**中的[为您的实例授权入站流量](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/authorizing-access-to-an-instance.html)。

请在以下情况下使用私有 IP 地址：
+ 您具有一个没有位于 Virtual Private Cloud (VPC) 中的 Amazon Redshift 集群和一个 Amazon EC2 -Classic 实例，二者位于同一 AWS 区域中。
+  您具有一个位于 VPC 中的 Amazon Redshift 集群和一个 Amazon EC2 -VPC 实例，二者位于同一 AWS 区域和同一 VPC 中。

 否则，请使用公有 IP 地址。

有关在 VPC 中使用 Amazon Redshift 的更多信息，请参阅《Amazon Redshift 管理指南》**中的[管理 Virtual Private Cloud (VPC) 中的集群](https://docs.aws.amazon.com/redshift/latest/mgmt/managing-clusters-vpc.html)。

## 步骤 4：获取主机的公有密钥
<a name="load-from-host-steps-get-the-host-key"></a>

您可以选择在清单文件中提供主机的公有密钥以便让 Amazon Redshift 可以标识主机。COPY 命令不需要主机公有密钥，但出于安全原因，我们强烈建议使用公有密钥来帮助防止“中间人”攻击。

您可以在以下位置查找主机的公有密钥（其中 `<ssh_host_rsa_key_name>` 是主机的公有密钥的唯一名称）：

```
:  /etc/ssh/<ssh_host_rsa_key_name>.pub
```

**注意**  
Amazon Redshift 仅支持 RSA 密钥。我们不支持 DSA 密钥。

在步骤 5 中创建清单文件时，您应将公有密钥的文本粘贴到清单文件条目中的“Public Key”字段中。

## 步骤 5：创建清单文件
<a name="load-from-host-steps-create-manifest"></a>

COPY 命令可连接到使用 SSH 的多台主机，并可以与每台主机建立多个 SSH 连接。COPY 通过每个主机连接运行一个命令，然后将来自这些命令的输出并行加载到表中。清单文件是 Amazon Redshift 用于连接主机的文本文件，采用 JSON 格式。清单文件指定 SSH 主机端点以及将在主机上运行的用于将数据返回到 Amazon Redshift 的命令。另外，您还可以包含主机公有密钥、登录用户名和每个条目的 mandatory 标志。

在本地计算机上创建清单文件。在后一个步骤中，将文件上载到 Amazon S3。

清单文件应采用以下格式：

```
{ 
   "entries": [ 
     {"endpoint":"<ssh_endpoint_or_IP>", 
       "command": "<remote_command>",
       "mandatory":true, 
       "publickey": "<public_key>", 
       "username": "<host_user_name>"}, 
     {"endpoint":"<ssh_endpoint_or_IP>", 
       "command": "<remote_command>",
       "mandatory":true, 
       "publickey": "<public_key>", 
       "username": "host_user_name"} 
    ] 
}
```

该清单文件为每个 SSH 连接包含一个 "entries" 结构。每个条目表示一个 SSH 连接。您可以与单台主机建立多个连接或与多台主机建立多个连接。如上所示，字段名称和值均需要使用双引号。唯一的一个不需要双引号的值是 mandatory 字段的布尔值 **true** 或 **false**。

下面描述了清单文件中的字段。

endpoint  
主机的 URL 地址或 IP 地址。例如，“`ec2-111-222-333.compute-1.amazonaws.com`”或“`22.33.44.56`”。

命令   
将由主机运行的命令，用于生成文本或二进制（gzip、lzop 或 bzip2）输出。该命令可以是用户 *"host\$1user\$1name"* 有权运行的任何命令。命令可以是像打印文件这样简单的命令，也可以查询数据库或启动脚本。输出（文本文件、gzip 二进制文件、lzop 二进制文件或 bzip2 二进制文件）必须采用 Amazon Redshift COPY 命令可摄取的形式。有关更多信息，请参阅 [准备输入数据](t_preparing-input-data.md)。

publickey  
（可选）主机的公有密钥。如果提供了公有密钥，Amazon Redshift 将使用它来标识主机。如果未提供公有密钥，Amazon Redshift 将不会尝试主机标识。例如，如果远程主机的公有密钥是 `ssh-rsa AbcCbaxxx…xxxDHKJ root@amazon.com`，请在公有密钥字段中输入以下文本：`AbcCbaxxx…xxxDHKJ`。

必需：  
（可选）指示在连接失败的情况下 COPY 命令是否应失败。默认值为 `false`。如果 Amazon Redshift 未成功建立至少一个连接，COPY 命令将失败。

username  
（可选）将用于登录到主机系统并运行远程命令的用户名。用户登录名必须与步骤 2 中用于将公有密钥添加到主机的授权密钥文件的登录名相同。默认用户名为“redshift”。

以下示例显示了用于与同一主机建立四个连接并通过每个连接运行不同的命令的完整清单：

```
{ 
  "entries": [ 
       {"endpoint":"ec2-184-72-204-112.compute-1.amazonaws.com", 
          "command": "cat loaddata1.txt", 
          "mandatory":true, 
          "publickey": "ec2publickeyportionoftheec2keypair", 
          "username": "ec2-user"}, 
       {"endpoint":"ec2-184-72-204-112.compute-1.amazonaws.com", 
          "command": "cat loaddata2.txt", 
          "mandatory":true, 
          "publickey": "ec2publickeyportionoftheec2keypair", 
          "username": "ec2-user"},
       {"endpoint":"ec2-184-72-204-112.compute-1.amazonaws.com", 
          "command": "cat loaddata3.txt", 
          "mandatory":true, 
          "publickey": "ec2publickeyportionoftheec2keypair", 
          "username": "ec2-user"},
       {"endpoint":"ec2-184-72-204-112.compute-1.amazonaws.com", 
          "command": "cat loaddata4.txt", 
          "mandatory":true, 
          "publickey": "ec2publickeyportionoftheec2keypair", 
          "username": "ec2-user"}
     ] 
}
```

## 步骤 6：将清单文件上载到 Amazon S3 桶
<a name="load-from-host-steps-upload-manifest"></a>

将清单文件上载到 Amazon S3 桶。如果 Amazon S3 桶不在您的 Amazon Redshift 集群所在的 AWS 区域内，则必须使用 [REGION](copy-parameters-data-source-s3.md#copy-region) 选项指定清单所在的 AWS 区域。有关创建 Amazon S3 桶并上载文件的信息，请参阅 [Amazon Simple Storage Service 用户指南](https://docs.aws.amazon.com/AmazonS3/latest/userguide/)。

## 步骤 7：运行 COPY 命令以加载数据
<a name="load-from-host-steps-run-copy"></a>

运行 [COPY](r_COPY.md) 命令以连接到主机并将数据加载到 Amazon Redshift 表中。在 COPY 命令中，指定清单文件的显式 Amazon S3 对象路径并包含 SSH 选项。例如，

```
COPY sales
FROM 's3://amzn-s3-demo-bucket/ssh_manifest'  
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole'
DELIMITER '|'
SSH;
```

**注意**  
如果使用自动压缩，则 COPY 命令将执行两次数据读取，这意味着它将运行两次远程命令。第一次读取用于提供压缩分析的样本，第二次读取实际加载数据。如果运行远程命令两次可能会由于潜在副作用而导致问题，则应关闭自动压缩。要关闭自动压缩，请运行 COPY 命令，同时将 COMPUPDATE 选项设置为 OFF。有关更多信息，请参阅 [使用自动压缩加载表](c_Loading_tables_auto_compress.md)。

# 从 Amazon DynamoDB 表中加载数据
<a name="t_Loading-data-from-dynamodb"></a>

您可以使用 COPY 命令从单个 Amazon DynamoDB 表加载带数据的表。

**重要**  
提供数据的 Amazon DynamoDB 表必须在与集群相同的 AWS 区域中创建，除非您使用 [REGION](copy-parameters-data-source-s3.md#copy-region) 选项指定 Amazon DynamoDB 表所在的 AWS 区域。

COPY 命令使用 Amazon Redshift 大规模并行处理 (MPP) 架构从 Amazon DynamoDB 表中并行读取和加载数据。您可以通过在 Amazon Redshift 表上设置分配样式，从而最大程度地利用并行处理。有关更多信息，请参阅 [用于优化查询的数据分配](t_Distributing_data.md)。

**重要**  
当 COPY 命令从 Amazon DynamoDB 表读取数据时，生成的数据传输是该表的预配置吞吐量的一部分。

为避免过度消耗预配置的读取吞吐量，我们建议您不要从生产环境中的 Amazon DynamoDB 表加载数据。如果您确实需要从生产表加载数据，我们建议您设置远低于未使用预配置吞吐量的平均百分比的 READRATIO 选项。低 READRATIO 设置有助于最大程度地减少限制问题。要使用 Amazon DynamoDB 表的所有预配置吞吐量，请将 READRATIO 设置为 100。

COPY 命令使用以下规则，将从 DynamoDB 表检索到的项目中的属性名称与现有 Amazon Redshift 表中的列名称匹配：
+ Amazon Redshift 表列与 Amazon DynamoDB 项目属性匹配且不区分大小写。如果 DynamoDB 表中的项目包含仅大小写不同的多个属性（如 Price 和 PRICE），COPY 命令将失败。
+ 与 Amazon DynamoDB 表中的属性不匹配的 Amazon Redshift 表列作为 NULL 或空值加载，具体取决于使用 [COPY](r_COPY.md) 命令中的 EMPTYASNULL 选项指定的值。
+ 将弃用与 Amazon Redshift 表中的列不匹配的 Amazon DynamoDB 属性。属性是在匹配前读取的，因此即使是已弃用的属性也会消耗该表的一部分预配置吞吐量。
+ 仅支持具有标量 STRING 和 NUMBER 数据类型的 Amazon DynamoDB 属性。不支持 Amazon DynamoDB BINARY 和 SET 数据类型。如果 COPY 命令尝试加载某个具有不支持的数据类型的属性，该命令将失败。如果该属性与 Amazon Redshift 表列不匹配，则 COPY 不会尝试加载它，它也不会引发错误。

COPY 命令使用以下语法从 Amazon DynamoDB 表加载数据：

```
COPY <redshift_tablename> FROM 'dynamodb://<dynamodb_table_name>'
authorization
readratio '<integer>';
```

*authorization* 的值是访问 Amazon DynamoDB 表所需的 AWS 凭证。如果这些凭证对应于某个用户，则该用户必须拥有对要加载的 Amazon DynamoDB 表执行 SCAN 和 DESCRIBE 的权限。

*authorization* 的值为您的集群提供访问 Amazon DynamoDB 表所需的 AWS 授权。必须包括对要加载的 Amazon DynamoDB 表执行 SCAN 和 DESCRIBE 的权限。有关所需权限的更多信息，请参阅[COPY、UNLOAD 和 CREATE LIBRARY 的 IAM 权限](copy-usage_notes-access-permissions.md#copy-usage_notes-iam-permissions)。进行身份验证的首选方法是为具有必要权限的 IAM 角色指定 IAM\$1ROLE 参数并提供 Amazon 资源名称（ARN）。有关更多信息，请参阅 [基于角色的访问控制](copy-usage_notes-access-permissions.md#copy-usage_notes-access-role-based)。

要使用 IAM\$1ROLE 参数进行身份验证，请替换 *<aws-account-id>* 和 *<role-name>*，如以下语法中所示。

```
IAM_ROLE 'arn:aws:iam::<aws-account-id>:role/<role-name>'
```

以下示例使用 IAM 角色进行身份验证。

```
COPY favoritemovies 
FROM 'dynamodb://ProductCatalog'
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole';
```

有关其他授权选项的更多信息，请参阅[授权参数](copy-parameters-authorization.md)。

如果您要验证您的数据而不实际加载表，请对 [COPY](r_COPY.md) 命令使用 NOLOAD 选项。

以下示例加载包含来自 DynamoDB 表 my-favorite-movies-table 的数据的 FAVORITEMOVIES 表。读取活动可消耗多达 50% 的预配置吞吐量。

```
COPY favoritemovies FROM 'dynamodb://my-favorite-movies-table' 
IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole' 
READRATIO 50;
```

为了最大程度地增加吞吐量，COPY 命令将跨集群中的计算节点从 Amazon DynamoDB 表并行加载数据。

## 预置的吞吐量与自动压缩
<a name="t_Loading-data-from-dynamodb-provisioned-throughput-with-automatic-compression"></a>

默认情况下，COPY 命令将在您指定没有压缩编码的空目标表时应用自动压缩。自动压缩分析最初将对 Amazon DynamoDB 表中的很多行进行采样。样本大小基于 COMPROWS 参数的值。默认值为每个切片 100000 行。

采样后，将弃用示例行并加载整个表。因此，很多行会被读取两次。有关自动压缩的工作方式的更多信息，请参阅[使用自动压缩加载表](c_Loading_tables_auto_compress.md)。

**重要**  
当 COPY 命令从 Amazon DynamoDB 表读取数据（包括用于采样的行）时，生成的数据传输是该表的预配置吞吐量的一部分。

## 从 Amazon DynamoDB 中加载多字节数据
<a name="t_Loading-data-from-dynamodb-loading-multibyte-data-from-amazon-dynamodb"></a>

如果您的数据包含非 ASCII 多字节字符（例如中文或西里尔语字符），则必须将该数据加载到 VARCHAR 列。VARCHAR 数据类型支持四字节的 UTF-8 字符，而 CHAR 数据类型仅接受单字节的 ASCII 字符。您不能将五字节或更长的字符加载到 Amazon Redshift 表中。有关 CHAR 和 VARCHAR 的更多信息，请参阅[数据类型](c_Supported_data_types.md)。

# 验证是否正确加载了数据
<a name="verifying-that-data-loaded-correctly"></a>

在完成加载操作后，请查询 [STL\$1LOAD\$1COMMITS](r_STL_LOAD_COMMITS.md) 系统表以验证所需文件是否已加载。在同一事务中运行 COPY 命令和加载验证，以便在加载出现问题时可以回滚整个事务。

以下查询将返回用于加载 TICKIT 数据库中的表的条目：

```
SELECT query, trim(filename) AS filename, curtime, status
FROM stl_load_commits
WHERE filename like '%tickit%' order by query;


 query |         filename          |          curtime           | status
-------+---------------------------+----------------------------+--------
 22475 | tickit/allusers_pipe.txt  | 2013-02-08 20:58:23.274186 |      1
 22478 | tickit/venue_pipe.txt     | 2013-02-08 20:58:25.070604 |      1
 22480 | tickit/category_pipe.txt  | 2013-02-08 20:58:27.333472 |      1
 22482 | tickit/date2008_pipe.txt  | 2013-02-08 20:58:28.608305 |      1
 22485 | tickit/allevents_pipe.txt | 2013-02-08 20:58:29.99489  |      1
 22487 | tickit/listings_pipe.txt  | 2013-02-08 20:58:37.632939 |      1
 22489 | tickit/sales_tab.txt      | 2013-02-08 20:58:37.632939 |      1
(6 rows)
```

# 验证输入数据
<a name="t_Validating_input_files"></a>

要在实际加载 Amazon S3 输入文件或 Amazon DynamoDB 表中的数据之前验证这些数据，请对 [COPY](r_COPY.md) 命令使用 NOLOAD 选项。将 NOLOAD 与您要用于加载数据的相同 COPY 命令和选项结合使用。NOLOAD 将检查所有数据的完整性而不用将其加载到数据库中。如果您尝试加载数据，NOLOAD 选项卡会显示将出现的任何错误。

例如，如果您为输入文件指定了不正确的 Amazon S3 路径，则 Amazon Redshift 将显示以下错误。

```
ERROR:  No such file or directory
DETAIL:
-----------------------------------------------
Amazon Redshift error:  The specified key does not exist
code:      2
context:   S3 key being read :
location:  step_scan.cpp:1883
process:   xenmaster [pid=22199]
-----------------------------------------------
```

要针对错误消息排错，请参阅[加载错误参考](r_Load_Error_Reference.md)。

有关使用 NOLOAD 选项的示例，请参阅 [带有 NOLOAD 选项的 COPY 命令](r_COPY_command_examples.md#r_COPY_command_examples-load-noload-option)。

# 使用自动压缩加载表
<a name="c_Loading_tables_auto_compress"></a>

您可以根据您自己的数据评估，手动将压缩编码应用于表中的各个列。或者，您可以使用 COPY 命令并将 COMPUPDATE 设置为 ON，以便根据示例数据自动分析和应用压缩。

您可以在创建并加载全新的表时使用自动压缩。COPY 命令可执行压缩分析。您也可以通过以下方式执行压缩分析而不加载数据或更改对表的压缩：对已填充的表运行 [ANALYZE COMPRESSION](r_ANALYZE_COMPRESSION.md) 命令。例如，您可以在想要分析对表的压缩以供将来使用时运行 ANALYZE COMPRESSION 命令，同时保留现有数据定义语言 (DDL) 语句。

自动压缩将在选择压缩编码时平衡整体性能。如果排序键列的压缩率远高于同一查询中的其他列，则范围受限扫描的执行效果可能会很差。因此，自动压缩会跳过排序键列上的数据分析阶段，并保留用户定义的编码类型。

如果您尚未明确定义某种编码，则自动压缩会选择 RAW 编码。ANALYZE COMPRESSION 的行为相同。要获得最佳查询性能，请考虑将 RAW 用于排序键。

## 自动压缩的工作方式
<a name="c_Loading_tables_auto_compress-how-automatic-compression-works"></a>

如果 COMPUPDATE 参数为 ON，每次为空目标表运行 COPY 命令并且所有表列具有 RAW 编码或没有编码时，COPY 命令将会应用自动压缩。

要将自动压缩应用于空表而不管其当前压缩编码如何，请运行 COPY 命令，同时将 COMPUPDATE 选项设置为 ON。要关闭自动压缩，请运行 COPY 命令，同时将 COMPUPDATE 选项设置为 OFF。

您不能将自动压缩应用于已包含数据的表。

**注意**  
自动压缩分析要求加载数据中有足够多的行（每个切片至少 100000 行），以生成有意义的样本。

作为加载事务的一部分，自动压缩将在后台执行以下操作：

1. 从输入文件加载初始的行样本。样本大小基于 COMPROWS 参数的值。默认值为 100000。

1. 为每个列选择压缩选项。

1. 从表中删除样本行。

1. 以所选的压缩编码重新创建表。

1. 使用新编码加载并压缩整个输入文件。

运行 COPY 命令后，表将会完全加载、压缩并可供使用。如果您在以后加载了更多数据，追加的行将根据现有编码进行压缩。

如果您只是想执行压缩分析，请运行 ANALYZE COMPRESSION，这比运行完整的 COPY 操作效率更高。然后，您可以评估结果以决定是使用自动压缩还是手动重新创建表。

只有 COPY 命令才支持自动压缩。或者，您也可以在创建表时手动应用压缩编码。有关手动压缩编码的信息，请参阅[列压缩，减小存储数据的大小](t_Compressing_data_on_disk.md)。

## 自动压缩示例
<a name="r_COPY_COMPRESS_examples"></a>

在本示例中，假定 TICKIT 数据库包含 LISTING 表的一个名为 BIGLIST 的副本，并且您希望在该表加载了大约 300 万行时对其应用自动压缩。

**加载和自动压缩表**

1. 确保表是空的。您只能将自动压缩应用于空表：

   ```
   TRUNCATE biglist;
   ```

1. 使用单个 COPY 命令加载表。尽管表是空的，但之前可能已指定某种编码。为便于 Amazon Redshift 执行压缩分析，请将 COMPUPDATE 参数设置为 ON。

   ```
   COPY biglist FROM 's3://amzn-s3-demo-bucket/biglist.txt' 
   IAM_ROLE 'arn:aws:iam::0123456789012:role/MyRedshiftRole'
   DELIMITER '|' COMPUPDATE ON;
   ```

   由于未指定 COMPROWS 选项，因此将使用默认和推荐的样本大小（100,000 行/切片）。

1. 查看 BIGLIST 表的新 schema 以了解自动选择的编码方案。

   ```
   SELECT "column", type, encoding 
   from pg_table_def where tablename = 'biglist';
   
   
        Column     |            Type             | Encoding 
   ----------------+-----------------------------+----------
    listid         | integer                     | az64
    sellerid       | integer                     | az64
    eventid        | integer                     | az64
    dateid         | smallint                    | none
    numtickets     | smallint                    | az64
    priceperticket | numeric(8,2)                | az64
    totalprice     | numeric(8,2)                | az64
    listtime       | timestamp without time zone | az64
   ```

1. 验证是否已加载所需的行数：

   ```
   select count(*) from biglist;
   
   count
   ---------
   3079952
   (1 row)
   ```

以后使用 COPY 或 INSERT 语句向此表追加行时，将应用相同的压缩编码。

# 针对窄表优化存储
<a name="c_load_compression_hidden_cols"></a>

如果您有一个列数非常少但行数非常多的表，三个隐藏的元数据标识列（INSERT\$1XID、DELETE\$1XID 和 ROW\$1ID）将为表消耗不成正比的磁盘空间。

 为了优化隐藏列的压缩，请尽可能在单个 COPY 事务中加载表。如果您使用多个单独的 COPY 命令加载表，INSERT\$1XID 列的压缩效果不会很好。如果您使用了多个 COPY 命令，则必须执行 vacuum 操作，但它不会改进 INSERT\$1XID 的压缩。

# 加载默认列值
<a name="c_loading_default_values"></a>

您可以选择在 COPY 命令中定义一个列列表。如果列列表忽略了表中的某个列，则 COPY 将用 CREATE TABLE 命令中指定的 DEFAULT 选项提供的值加载该列，或加载 NULL（如果未指定 DEFAULT 选项）。

如果 COPY 尝试将 NULL 分配到一个定义为 NOT NULL 的列，COPY 命令将失败。有关分配 DEFAULT 选项的信息，请参阅 [CREATE TABLE](r_CREATE_TABLE_NEW.md)。

当从 Amazon S3 上的数据文件加载时，列列表中的列必须与数据文件中字段的顺序相同。如果数据文件中的某个字段在列列表中没有对应的列，COPY 命令将失败。

从 Amazon DynamoDB 表加载时，顺序并不重要。将弃用与 Amazon Redshift 表中的列不匹配的 Amazon DynamoDB 属性中的任何字段。

当使用 COPY 命令将 DEFAULT 值加载到表中时，将适用以下限制：
+ 如果 [IDENTITY](r_CREATE_TABLE_NEW.md#identity-clause) 列包含在列列表中，则还必须在 [COPY](r_COPY.md) 命令中指定 EXPLICIT\$1IDS 选项，否则 COPY 命令将失败。同样，如果列列表省略了 IDENTITY 列，并且指定了 EXPLICIT\$1IDS 选项，COPY 操作将失败。
+ 由于给定列的已计算 DEFAULT 表达式对所有已加载行是相同的，因此，使用 RANDOM() 函数的 DEFAULT 表达式会向所有行分配相同的值。
+ 包含 CURRENT\$1DATE 或 SYSDATE 的 DEFAULT 表达式将设置为当前事务的时间戳。

有关示例，请参阅 [COPY 示例](r_COPY_command_examples.md)中的“从带有默认值的文件加载数据”。

# 解决数据加载问题
<a name="t_Troubleshooting_load_errors"></a>

在将数据加载到 Amazon Redshift 表时，可能会遇到来自 Amazon S3 的错误、无效的输入数据和 COPY 命令错误。以下各节提供了有关识别和解决数据加载错误的信息。

**Topics**
+ [对 S3 事件集成和 COPY JOB 错误进行故障排除](s3-integration-troubleshooting.md)
+ [S3ServiceException 错误](s3serviceexception-error.md)
+ [用于解决数据加载问题的系统表](system-tables-for-troubleshooting-data-loads.md)
+ [多字节字符加载错误](multi-byte-character-load-errors.md)
+ [加载错误参考](r_Load_Error_Reference.md)

# 对 S3 事件集成和 COPY JOB 错误进行故障排除
<a name="s3-integration-troubleshooting"></a>

使用以下信息来排查与 Amazon S3 事件集成和 Amazon Redshift 的 COPY JOB 相关的常见问题。

## S3 事件集成创建失败
<a name="s3-integration-troubleshooting-creation"></a>

如果 S3 事件集成创建失败，则集成的状态为 `Inactive`。请确保 Amazon Redshift 数据仓库在以下方面正确无误。
+ 您为 Amazon Redshift 中的目标命名空间添加了正确的已授权主体和集成源。请参阅[创建 S3 事件集成的先决条件](loading-data-copy-job.md#loading-data-copy-job-prerequisites)。
+ 您已将基于资源的正确策略添加到源 Amazon S3 存储桶。请参阅[创建 S3 事件集成的先决条件](loading-data-copy-job.md#loading-data-copy-job-prerequisites)。

## Amazon S3 数据未出现在目标数据库中
<a name="s3-integration-troubleshooting-missing-data"></a>

如果来自 COPY JOB 的数据未出现，请检查以下各项。
+ 查询 SYS\$1COPY\$1JOB\$1DETAIL 以查看 Amazon S3 文件是否已加载，是否等待摄取，或者是否存在错误。有关更多信息，请参阅 [SYS\$1COPY\$1JOB\$1DETAIL](SYS_COPY_JOB_DETAIL.md)。
+ 如果 Amazon S3 文件不存在或等待时间不符合预期，请参阅 STL\$1ERROR 或 SYS\$1COPY\$1JOB\$1INFO。查找凭证错误或任何表明集成处于非活动状态的内容。有关更多信息，请参阅[STL\$1ERROR](r_STL_ERROR.md)和[SYS\$1COPY\$1JOB\$1INFO](SYS_COPY_JOB_INFO.md)。

# S3ServiceException 错误
<a name="s3serviceexception-error"></a>

最常见的 s3ServiceException 错误由以下原因导致：凭证字符串的格式有误或凭证字符串不正确，将集群和桶放在不同的 AWS 区域中以及 Amazon S3 权限不足。

本节提供了每种类型的错误的排查信息。

## 凭证字符串无效
<a name="invalid-credentials-string-error"></a>

如果凭证字符串的格式不正确，您将收到以下错误消息：

```
ERROR: Invalid credentials. Must be of the format: credentials 
'aws_access_key_id=<access-key-id>;aws_secret_access_key=<secret-access-key>
[;token=<temporary-session-token>]'
```

请验证凭证字符串不包含任何空格或换行符，并且括在单引号中。

## 访问密钥 ID 无效
<a name="invalid-access-key-id-error"></a>

如果您的访问密钥 ID 不存在，您将收到以下错误消息：

```
[Amazon](500310) Invalid operation: S3ServiceException:The AWS Access Key Id you provided does not exist in our records.
```

这通常是复制和粘贴错误。请验证是否正确输入了访问密钥 ID。此外，如果您使用的是临时会话密钥，请检查是否已设置 `token` 的值。

## 秘密访问密钥无效
<a name="invalid-secret-access-key-error"></a>

如果您的秘密访问密钥不正确，您将收到以下错误消息：

```
[Amazon](500310) Invalid operation: S3ServiceException:The request signature we calculated does not match the signature you provided. 
Check your key and signing method.,Status 403,Error SignatureDoesNotMatch
```

这通常是复制和粘贴错误。请验证是否正确输入了秘密访问密钥，并验证它是否为访问密钥 ID 对应的正确密钥。

## 桶位于不同的区域
<a name="bucket-in-different-region"></a>

COPY 命令中指定的 Amazon S3 桶必须位于集群所在的 AWS 区域。如果 Amazon S3 桶和集群位于不同的区域中，则会收到类似于以下内容的错误：

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

您可以通过以下方式在特定区域中创建 Amazon S3 桶：在使用 Amazon S3 管理控制台创建桶时选择该区域，或者在使用 Amazon S3 API 或 CLI 创建桶时指定端点。有关更多信息，请参阅 [将文件上传到 Amazon S3 以与 COPY 结合使用](t_uploading-data-to-S3.md)。

有关 Amazon S3 区域的更多信息，请参阅《Amazon Simple Storage Service 用户指南》**中的[访问桶](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingBucket.html#access-bucket-intro)。

或者，您也可以在 COPY 命令中使用 [REGION](copy-parameters-data-source-s3.md#copy-region) 选项以指定区域。

## 访问被拒绝
<a name="s3-access-denied-error"></a>

如果用户没有足够的权限，您将收到以下错误消息：

```
ERROR: S3ServiceException:Access Denied,Status 403,Error AccessDenied
```

一个可能的原因是，凭证标识的用户没有对 Amazon S3 桶的 LIST 和 GET 访问权限。有关其他原因，请参阅《Amazon Simple Storage Service 用户指南》**中的[排查 Amazon S3 中的拒绝访问（403 禁止）错误](https://docs.aws.amazon.com/AmazonS3/latest/userguide/troubleshoot-403-errors.html)。

有关管理用户对桶的访问权限的信息，请参阅《Amazon Simple Storage Service 用户指南》**中的 [Amazon S3 中的身份和访问管理](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-access-control.html)。

# 用于解决数据加载问题的系统表
<a name="system-tables-for-troubleshooting-data-loads"></a>

以下 Amazon Redshift 系统表可能有助于排查数据加载问题：
+ 查询 [STL\$1LOAD\$1ERRORS](r_STL_LOAD_ERRORS.md) 以发现在特定加载期间发生的错误。
+ 查询 [STL\$1FILE\$1SCAN](r_STL_FILE_SCAN.md) 以查看特定文件的加载时间或了解是否甚至读取了某个特定文件。
+ 查询 [STL\$1S3CLIENT\$1ERROR](r_STL_S3CLIENT_ERROR.md) 以查找有关从 Amazon S3 传输数据时遇到的错误的详细信息。

**查找和诊断加载错误**

1. 创建视图或定义返回有关加载错误的详细信息的查询。以下示例将 STL\$1LOAD\$1ERRORS 表联接到 STV\$1TBL\$1PERM 表以将表 ID 与实际表名称进行匹配。

   ```
   create view loadview as
   (select distinct tbl, trim(name) as table_name, query, starttime,
   trim(filename) as input, line_number, colname, err_code,
   trim(err_reason) as reason
   from stl_load_errors sl, stv_tbl_perm sp
   where sl.tbl = sp.id);
   ```

1. 将 COPY 命令中的 MAXERRORS 选项设置为足够大的值，以使 COPY 能够返回有关您的数据的有用值。如果 COPY 遇到错误，则会出现一条错误消息，指示您参阅 STL\$1LOAD\$1ERRORS 表以了解详细信息。

1. 查询 LOADVIEW 视图以查看错误详细信息。例如：

   ```
   select * from loadview where table_name='venue';
   ```

   ```
     tbl   | table_name | query |         starttime          
   --------+------------+-------+----------------------------
    100551 | venue      | 20974 | 2013-01-29 19:05:58.365391 
   
   |     input      | line_number | colname | err_code |       reason
   +----------------+-------------+---------+----------+--------------------
   | venue_pipe.txt |           1 |       0 |     1214 | Delimiter not found
   ```

1. 根据视图返回的信息修复输入文件或加载脚本中的问题。要观察的一些典型加载错误包括：
   + 表中的数据类型与输入数据字段中的值不匹配。
   + 表中的列数与输入数据中的字段数不匹配。
   + 引号不匹配。Amazon Redshift 支持单引号和双引号；但是，必须适当平衡这些引号。
   + 输入文件中的日期/时间数据的格式不正确。
   + 输入文件中的值超出范围（对于数字列）。
   + 某个列的不同值的数量超出了对其压缩编码的限制。

# 多字节字符加载错误
<a name="multi-byte-character-load-errors"></a>

带有 CHAR 数据类型的列仅接受单字节 UTF-8 字符（字节值最大为 127，即十六进制的 7F），它也是 ASCII 字符集。VARCHAR 列接受多字节 UTF-8 字符，最多四个字节。有关更多信息，请参阅 [字符类型](r_Character_types.md)。

如果加载数据中的某一行包含对列数据类型无效的字符，则 COPY 将返回一个错误并在 STL\$1LOAD\$1ERRORS 系统日志表中记录一行，错误编号为 1220。ERR\$1REASON 字段包含无效字符的十六进制字节序列。

修复加载数据中的无效字符的替代方法是在加载过程中替换无效字符。要替换无效的 UTF-8 字符，请在 COPY 命令中指定 ACCEPTINVCHARS 选项。如果设置了 ACCEPTINVCHARS 选项，那么指定的字符将替换代码点。如果未设置 ACCEPTINVCHARS 选项，Amazon Redshift 会接受这些字符作为有效的 UTF-8。有关更多信息，请参阅 [ACCEPTINVCHARS](copy-parameters-data-conversion.md#acceptinvchars)。

以下代码点列表是有效的 UTF-8，如果未设置 ACCEPTINVCHARS 选项，COPY 操作不会返回错误。但是，这些代码点是无效字符。您可以使用 [ACCEPTINVCHARS](copy-parameters-data-conversion.md#acceptinvchars) 选项用指定的字符替换代码点。这些代码点的值范围为 `0xFDD0` 到 `0xFDEF`，最高值为 `0x10FFFF`，以 `FFFE` 或 `FFFF` 结尾：
+ `0xFFFE`, `0x1FFFE`, `0x2FFFE`, …, `0xFFFFE`, `0x10FFFE`
+ `0xFFFF`, `0x1FFFF`, `0x2FFFF`, …, `0xFFFFF`, `0x10FFFF`

以下示例显示了 COPY 尝试将 UTF-8 字符 `e0 a1 c7a4` 加载到 CHAR 列中时的错误原因。

```
Multibyte character not supported for CHAR 
(Hint: Try using  VARCHAR). Invalid char: e0 a1 c7a4
```

如果错误与 VARCHAR 数据类型相关，则错误原因将包含错误代码和无效的 UTF-8 十六进制序列。以下示例显示了 COPY 尝试将 UTF-8 `a4` 加载到 VARCHAR 字段中时的错误原因。

```
String contains invalid or unsupported UTF-8 codepoints. 
Bad UTF-8 hex sequence: a4 (error 3)
```

下表列出了 VARCHAR 加载错误的描述和建议解决方法。如果出现了以下错误之一，请将该字符替换为有效的 UTF-8 代码序列或删除该字符。

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/multi-byte-character-load-errors.html)

# 加载错误参考
<a name="r_Load_Error_Reference"></a>

如果在从文件中加载数据时出现任何错误，请查询 [STL\$1LOAD\$1ERRORS](r_STL_LOAD_ERRORS.md) 表以识别错误并确定可能的解释。下表列出了在数据加载期间可能出现的所有错误代码：

## 加载错误代码
<a name="r_Load_Error_Reference-load-error-codes"></a>

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

# 创建 S3 事件集成以自动从 Amazon S3 存储桶复制文件
<a name="loading-data-copy-job"></a>

**注意**  
自动复制的预览版已结束。因此，预览版集群将在预览期结束后 30 天被自动移除。如果您计划继续使用自动复制，我们建议您在另一个 Amazon Redshift 集群上重新创建现有的自动复制作业。不支持将预览版集群升级到最新的 Amazon Redshift 版本。

可以使用自动复制作业，来将数据从存储在 Amazon S3 中的文件加载到 Amazon Redshift 表中。Amazon Redshift 会检测新的 Amazon S3 文件何时添加到 COPY 命令中指定的路径。然后，无需创建外部数据摄取管道，即可自动运行 COPY 命令。Amazon Redshift 会跟踪已加载哪些文件。Amazon Redshift 确定每个 COPY 命令一起进行批处理的文件数。您可以在系统视图中看到生成的 COPY 命令。

创建自动 COPY JOB 的第一步是创建 S3 事件集成。当 Amazon S3 源存储桶中出现新文件时，Amazon Redshift 会使用 COPY 命令管理将文件加载到数据库中的过程。

## 创建 S3 事件集成的先决条件
<a name="loading-data-copy-job-prerequisites"></a>

要设置 S3 事件集成，请确认已完成以下先决条件。
+ Amazon S3 存储桶必须具有支持多个 Amazon S3 权限的存储桶策略。例如，以下示例策略支持对托管在 *us-east-1* 中的资源存储桶 `amzn-s3-demo-bucket` 拥有权限。Amazon S3 存储桶和集成位于同一 AWS 区域中。

------
#### [ JSON ]

****  

  ```
  {
      "Version":"2012-10-17",		 	 	 
      "Statement": [
          {
              "Sid": "Auto-Copy-Policy-01",
              "Effect": "Allow",
              "Principal": {
                  "Service": "redshift.amazonaws.com"
                  },
              "Action": [
                  "s3:GetBucketNotification",
                  "s3:PutBucketNotification",
                  "s3:GetBucketLocation"
              ],
              "Resource": "arn:aws:s3:::amzn-s3-demo-bucket:*",
              "Condition": {
                  "ArnLike": {
                      "aws:SourceArn": "arn:aws:redshift:us-east-1:111122223333:integration:*"
                  },
                  "StringEquals": {
                      "aws:SourceAccount": "111122223333"
                  }
              }
          }
      ]
  }
  ```

------
+ 目标 Amazon Redshift 预置集群或 Redshift Serverless 命名空间必须对存储桶具有权限。确认与集群或无服务器命名空间关联的 IAM 角色具有支持适当权限的 IAM 策略。该策略必须对诸如 `amzn-s3-demo-bucket` 之类的存储桶资源支持 `s3:GetObject`，并对诸如 `amzn-s3-demo-bucket/*` 之类的存储桶资源及其内容支持 `s3:ListBucket`。

------
#### [ JSON ]

****  

  ```
  {
      "Version":"2012-10-17",		 	 	 
      "Statement": [
          {
              "Sid": "AutoCopyReadId",
              "Effect": "Allow",
              "Action": [
                  "s3:GetObject",
                  "s3:ListBucket"
              ],
              "Resource": [
                  "arn:aws:s3:::amzn-s3-demo-bucket",  
                  "arn:aws:s3:::amzn-s3-demo-bucket/*" 
              ]
          }
      ]
  }
  ```

------

  将策略添加到具有角色信任关系的 IAM 角色中，如下所示。

------
#### [ JSON ]

****  

  ```
  {
      "Version":"2012-10-17",		 	 	 
      "Statement": [
          {
              "Effect": "Allow",
              "Principal": {
                  "Service": [
                      "redshift.amazonaws.com"
                  ]
              },
              "Action": "sts:AssumeRole"
          }
      ]
  }
  ```

------

  如果目标数据仓库是预置集群，则可以使用 Amazon Redshift 控制台的集群详细信息中的**集群权限**选项卡，将 IAM 角色与预置集群关联。有关如何将角色与预置集群关联的信息，请参阅《Amazon Redshift 管理指南》**中的[将 IAM 角色与集群相关联](https://docs.aws.amazon.com/redshift/latest/mgmt/copy-unload-iam-role-associating-with-clusters.html)。

  如果目标数据仓库是 Redshift Serverless，则可以使用 Redshift Serverless 控制台的命名空间详细信息中的**安全和加密**选项卡，将 IAM 角色与无服务器命名空间关联。有关如何将角色与无服务器命名空间关联的信息，请参阅《Amazon Redshift 管理指南》**中的[向 Amazon Redshift Serverless 授予权限](https://docs.aws.amazon.com/redshift/latest/mgmt/serverless-security-other-services.html)。
+ Amazon Redshift 数据仓库还必须具有支持 Amazon S3 存储桶的资源策略。如果您使用 Amazon Redshift 控制台，当您创建 S3 事件集成时，Amazon Redshift 会提供**为我修复它**选项，以便将此策略添加到 Amazon Redshift 数据仓库。要自行更新资源策略，可以使用 [put-resource-policy](https://docs.aws.amazon.com/cli/latest/reference/redshift/put-resource-policy.html) AWS CLI 命令。例如，要将资源策略附加到 Amazon Redshift 预置集群，以便将 S3 事件与 Amazon S3 存储桶集成，请运行类似于以下内容的 AWS CLI 命令。以下示例显示了 *us-east-1* AWS 区域中用户账户 *123456789012* 的预置集群命名空间的策略。存储桶名为 *amzn-s3-demo-bucket*。

  ```
  aws redshift put-resource-policy \
  --policy file://rs-rp.json \
  --resource-arn "arn:aws:redshift: us-east-1:123456789012:namespace/cc4ffe56-ad2c-4fd1-a5a2-f29124a56433"
  ```

  其中 `rs-rp.json` 包含：

------
#### [ JSON ]

****  

  ```
  {
  	"Version":"2012-10-17",		 	 	 
  	"Statement": [
  		{
  			"Effect": "Allow",
  			"Principal": {
  				"Service": "redshift.amazonaws.com"
  			},
  			"Action": "redshift:AuthorizeInboundIntegration",
  			"Resource": "arn:aws:redshift:us-east-1:123456789012:namespace:cc4ffe56-ad2c-4fd1-a5a2-f29124a56433",
  			"Condition": {
  				"StringEquals": {
  					"aws:SourceArn": "arn:aws:s3:::amzn-s3-demo-bucket",
  					"aws:SourceAccount": 111122223333
  				}
  			}
  		},
  		{
  			"Effect": "Allow",
  			"Principal": {
  				"AWS": "arn:aws:iam::111122223333:role/myRedshiftRole"
  			},
  			"Action": "redshift:CreateInboundIntegration",
  			"Resource": "arn:aws:redshift:us-east-1:123456789012:namespace:cc4ffe56-ad2c-4fd1-a5a2-f29124a56433",
  			"Condition": {
  				"StringEquals": {
  					"aws:SourceArn": "arn:aws:s3:::amzn-s3-demo-bucket",
  					"aws:SourceAccount": 111122223333
  				}
  			}
  		}
  	]
  }
  ```

------

  要将资源策略附加到 Redshift Serverless 命名空间，以便将 S3 事件与 Amazon S3 存储桶集成，请运行类似于以下内容的 AWS CLI 命令。以下示例显示了 *us-east-1* AWS 区域中用户账户 *123456789012* 的无服务器命名空间的策略。存储桶名为 *amzn-s3-demo-bucket*。

  ```
  aws redshift put-resource-policy \
  --policy file://rs-rp.json \
  --resource-arn "arn:aws:redshift-serverless:us-east-1:123456789012:namespace/namespace-1"
  ```

  其中 `rs-rp.json` 包含：

------
#### [ JSON ]

****  

  ```
  {
  	"Version":"2012-10-17",		 	 	 
  	"Statement": [
  		{
  			"Effect": "Allow",
  			"Principal": {
  				"Service": "redshift.amazonaws.com"
  			},
  			"Action": "redshift:AuthorizeInboundIntegration",
  			"Resource": "arn:aws:redshift-serverless:us-east-1:123456789012:namespace/namespace-1",
  			"Condition": {
  				"StringEquals": {
  					"aws:SourceArn": "arn:aws:s3:::amzn-s3-demo-bucket",
  					"aws:SourceAccount": 111122223333
  	
  				}
  			}
  		},
  		{
  			"Effect": "Allow",
  			"Principal": {
  				"AWS": "arn:aws:iam::123456789012:user/myUser"
  			},
  			"Action": "redshift:CreateInboundIntegration",
  			"Resource": "arn:aws:redshift-serverless:us-east-1:123456789012:namespace/namespace-1",
  			"Condition": {
  				"StringEquals": {
  					"aws:SourceArn": "arn:aws:s3:::amzn-s3-demo-bucket",
  					"aws:SourceAccount": 111122223333
  				}
  			}
  		}
  	]
  }
  ```

------

## 创建 S3 事件集成
<a name="loading-data-copy-job-create-s3-event-integration"></a>

要设置复制作业，请先定义 S3 事件集成。

------
#### [ Amazon Redshift console ]

**在 Amazon Redshift 控制台上创建 Amazon S3 事件集成**

1. 登录到 AWS 管理控制台并打开 Amazon Redshift 控制台，网址：[https://console.aws.amazon.com/redshiftv2/](https://console.aws.amazon.com/redshiftv2/)。

1. 在左侧导航窗格中，选择 **S3 事件集成**。

1. 选择**创建 Amazon S3 事件集成**以打开向导来进行创建，并选择要用于自动复制的 S3 事件集成。源 Amazon S3 存储桶和目标 Amazon Redshift 数据仓库必须位于同一 AWS 区域中。在执行创建集成的步骤时，请指定以下信息：
   + **集成名称** - 是当前 AWS 区域中您的 AWS 账户拥有的跨所有集成的唯一标识符。
   + **描述** – 是描述 Amazon S3 事件集成的文本，供以后参考。
   + **源 S3 存储桶** – 是当前 AWS 账户和 AWS 区域中的 Amazon S3 存储桶，它是将数据摄取到 Amazon Redshift 的源。
   + **Amazon Redshift 数据仓库** – 是从集成中接收数据的目标 Amazon Redshift 预置集群或 Redshift Serverless 工作组。

     如果您的目标 Amazon Redshift 在同一个账户中，则可以选择目标。如果目标位于另一个账户中，则需要指定 **Amazon Redshift 数据仓库 ARN**。目标必须具有包含授权主体和集成源的资源策略。如果目标上没有正确的资源策略，并且您的目标在同一个账户中，则可以选择**自动修复此问题**选项，以便在创建集成过程中自动应用资源策略。如果您的目标位于另一个 AWS 账户，则需要手动在 Amazon Redshift 仓库中应用资源策略。

1. 输入最多 50 个标签**键**和一个可选**值** - 提供有关集成的其他元数据。

1. 此时会显示一个检查页面，您可以在这个页面中选择**创建 S3 事件集成**。

------
#### [ AWS CLI ]

要使用 AWS CLI 创建 Amazon S3 事件集成，请使用带有以下选项的 `create-integration` 命令：
+ `integration-name` – 指定集成的名称。
+ `source-arn` – 指定 Amazon S3 源存储桶的 ARN。
+ `target-arn` - 指定 Amazon Redshift 预置集群或 Redshift Serverless 工作组目标的命名空间 ARN。

以下示例通过提供集成名称、源 ARN 和目标 ARN 来创建集成。集成未加密。

```
aws redshift create-integration \
--integration-name s3-integration \
--source-arn arn:aws:s3:us-east-1::s3-example-bucket \
--target-arn arn:aws:redshift:us-east-1:123456789012:namespace:a1b2c3d4-5678-90ab-cdef-EXAMPLE22222
          {
    "IntegrationArn": "arn:aws:redshift:us-east-1:123456789012:integration:a1b2c3d4-5678-90ab-cdef-EXAMPLE11111",
    "IntegrationName": "s3-integration",
    "SourceArn": "arn:aws:s3:::s3-example-bucket",
    "SourceType": "s3-event-notifications",
    "TargetArn": "arn:aws:redshift:us-east-1:123456789012:namespace:a1b2c3d4-5678-90ab-cdef-EXAMPLE22222",
    "Status": "creating",
    "Errors": [],
    "CreateTime": "2024-10-09T19:08:52.758000+00:00",
    "Tags": []
}
```

也可以使用以下 AWS CLI 命令来管理 S3 事件集成。
+ `delete-integration` – 指定集成 ARN 以删除 S3 事件集成。
+ `modify-integration` – 指定集成 ARN 以更改 S3 事件集成的名称和/或描述。
+ `describe-integrations` – 指定集成 ARN 以查看 S3 事件集成的属性。

有关这些命令的更多信息，请参阅 [https://docs.aws.amazon.com/cli/latest/reference/redshift/](https://docs.aws.amazon.com/cli/latest/reference/redshift/)。

------

然后，Amazon Redshift 创建一个 S3 事件集成，并包含其关联的源和目标、状态以及有关关联的自动复制作业状态的信息。可以在 Amazon Redshift 控制台上查看有关 S3 事件集成的信息，方法是选择 **S3 事件集成**，然后选择要显示其详细信息的集成。集成按**在我的账户中**和**来自其它账户**创建的集成分开。**在我的账户中**列表显示源和目标位于同一个账户中的集成。**来自其它账户**列表显示源归其它账户拥有的集成。

如果您删除 S3 事件集成，则相应的 COPY JOB 状态将从 `1`（活动）更改为 `0`（非活动/待处理）。但是，不会自动删除相应的 COPY JOB。如果您稍后尝试创建同名的 COPY JOB，则可能会发生冲突。

## 创建和监控 COPY JOB
<a name="loading-data-copy-job-create-s3-autocopy"></a>

创建集成后，在您创建的集成的 **S3 事件集成详细信息**页面上，选择**创建自动复制作业**以转至 Amazon Redshift 查询编辑器 V2，您可以在其中为集成创建自动复制作业。Amazon Redshift 将 COPY JOB CREATE 语句的 FROM 子句中的存储桶与 S3 事件集成中使用的存储桶进行匹配。有关如何使用 Amazon Redshift 查询编辑器 V2 的信息，请参阅《Amazon Redshift 管理指南》**中的[使用 Amazon Redshift 查询编辑器 V2 查询数据库](https://docs.aws.amazon.com/redshift/latest/mgmt/query-editor-v2.html)。例如，在查询编辑器 V2 中运行以下 COPY 命令来创建自动 COPY JOB，该作业将 Amazon S3 存储桶 `s3://amzn-s3-demo-bucket/staging-folder` 与 Amazon S3 事件集成相匹配。

```
COPY public.target_table
FROM 's3://amzn-s3-demo-bucket/staging-folder'
IAM_ROLE 'arn:aws:iam::123456789012:role/MyLoadRoleName'
JOB CREATE my_copy_job_name
AUTO ON;
```

COPY JOB 只需定义一次。未来运行使用相同的参数。

要定义和管理 COPY JOB，您必须拥有权限。有关授予和撤销对 COPY JOB 的权限的信息，请参阅 [GRANT](r_GRANT.md) 和 [REVOKE](r_REVOKE.md)。有关授予和撤销对 COPY JOB 的范围限定权限的更多信息，请参阅[授予限定范围权限](r_GRANT.md#grant-scoped-syntax)和[撤销限定范围权限](r_REVOKE.md#revoke-scoped-permissions)。

您可以使用 CREATE、LIST、SHOW、DROP、ALTER 和 RUN 作业的选项来管理加载操作。有关更多信息，请参阅 [COPY JOB](r_COPY-JOB.md)。

您可以查询系统视图以查看 COPY JOB 状态和进度。提供的视图如下：
+ [SYS\$1COPY\$1JOB](SYS_COPY_JOB.md) – 为当前定义的每个 COPY JOB 包含一行。
+ [SYS\$1COPY\$1JOB\$1DETAIL](SYS_COPY_JOB_DETAIL.md) – 包含每个 COPY JOB 的待处理、错误和已摄取文件的详细信息。
+ [SYS\$1COPY\$1JOB\$1INFO](SYS_COPY_JOB_INFO.md) – 包含记录的有关 COPY JOB 的消息。
+ [SYS\$1LOAD\$1HISTORY](SYS_LOAD_HISTORY.md) – 包含 COPY 命令的详细信息。
+ [SYS\$1LOAD\$1ERROR\$1DETAIL](SYS_LOAD_ERROR_DETAIL.md) – 包含 COPY 命令错误的详细信息。
+ [SVV\$1COPY\$1JOB\$1INTEGRATIONS](SVV_COPY_JOB_INTEGRATIONS.md) – 包含 S3 事件集成的详细信息。
+ [STL\$1LOAD\$1ERRORS](r_STL_LOAD_ERRORS.md) – 包含 COPY 命令中的错误。
+ [STL\$1LOAD\$1COMMITS](r_STL_LOAD_COMMITS.md) – 包含用于对 COPY 命令数据加载进行故障排除的信息。

有关排查 S3 事件集成错误的信息，请参阅[对 S3 事件集成和 COPY JOB 错误进行故障排除](s3-integration-troubleshooting.md)。

要获取 COPY JOB 所加载文件的列表，请运行以下 SQL，但首先替换 *<job\$1id>*：

```
SELECT job_id, job_name, data_source, copy_query, filename, status, curtime
FROM sys_copy_job copyjob
JOIN stl_load_commits loadcommit
ON copyjob.job_id = loadcommit.copy_job_id
WHERE job_id = <job_id>;
```

## 为自动复制创建 S3 事件集成时的注意事项
<a name="loading-data-copy-job-considerations"></a>

使用自动复制时，请注意以下事项。
+ 在一个 AWS 账户中，可以为每个集群或工作组创建最多 200 个 COPY JOB。
+ 可以为每个 Amazon Redshift 目标创建最多 50 个 S3 事件集成。
+ 无法使用在存储桶名称中包含句点（.）的源 Amazon S3 存储桶来创建 S3 事件集成。
+ 您只能在相同的源和目标之间创建一个 S3 事件集成。也就是说，一次只能在 Amazon S3 存储桶和 Amazon Redshift 数据仓库之间创建一个 S3 事件集成。
+ 对于在源 Amazon S3 存储桶上定义的事件类型 `S3_OBJECT_CREATED`，您无法获得任何现有的事件通知。不过，在创建 S3 事件集成后，您可以使用范围较小的前缀/后缀来更新 Amazon S3 存储桶事件通知。这样一来，您还可以针对其他目标配置另一个前缀/后缀的 `S3_OBJECT_CREATED`，避免与 S3 事件集成发生冲突。如果您遇到导致自动复制无法按预期运行的问题，请在联系 AWS 支持 时为相关时间段准备 S3 存储桶上的 `s3:PutBucketNotificationConfiguration` 操作的 AWS CloudTrail 日志。

## 支持的区域
<a name="loading-data-copy-job-regions"></a>

自动复制可在以下区域中使用。


| 区域 | 自动复制 | 
| --- | --- | 
| 非洲（开普敦） | 可用 | 
| 亚太地区（香港） | 可用 | 
| 亚太地区（台北） | 可用 | 
| 亚太地区（东京） | 可用 | 
| 亚太地区（首尔） | 可用 | 
| 亚太地区（大阪） | 可用 | 
| 亚太地区（孟买） | 可用 | 
| 亚太地区（海得拉巴） | 可用 | 
| 亚太地区（新加坡） | 可用 | 
| 亚太地区（悉尼） | 可用 | 
| 亚太地区（雅加达） | 可用 | 
| 亚太地区（墨尔本） | 可用 | 
| 亚太地区（马来西亚） | 可用 | 
| 亚太地区（新西兰） | 不可用 | 
| 亚太地区（泰国） | 可用 | 
| 加拿大（中部） | 可用 | 
| 加拿大西部（卡尔加里） | 可用 | 
| 中国（北京） | 可用 | 
| 中国（宁夏） | 可用 | 
| 欧洲地区（法兰克福） | 可用 | 
| 欧洲（苏黎世） | 可用 | 
| 欧洲地区（斯德哥尔摩） | 可用 | 
| 欧洲地区（米兰） | 可用 | 
| 欧洲（西班牙） | 可用 | 
| 欧洲地区（爱尔兰） | 可用 | 
| 欧洲地区（伦敦） | 可用 | 
| 欧洲地区（巴黎） | 可用 | 
| 以色列（特拉维夫） | 可用 | 
| 中东（阿联酋） | 可用 | 
| 中东（巴林） | 可用 | 
| 墨西哥（中部） | 可用 | 
| 南美洲（圣保罗） | 可用 | 
| 美国东部（弗吉尼亚州北部） | 可用 | 
| 美国东部（俄亥俄州） | 可用 | 
| 美国西部（北加利福尼亚） | 可用 | 
| 美国西部（俄勒冈州） | 可用 | 
| AWS GovCloud（美国东部） | 可用 | 
| AWS GovCloud（美国西部） | 可用 | 

# 使用 DML 命令加载表
<a name="t_Updating_tables_with_DML_commands"></a>

Amazon Redshift 支持标准数据操作语言 (DML) 命令（INSERT、UPDATE 和 DELETE），您可以使用这些命令修改表中的行。您还可使用 TRUNCATE 命令执行快速批量删除。

**注意**  
我们强烈建议您使用 [COPY](r_COPY.md) 命令来加载大量数据。使用单个 INSERT 语句填充表可能过于缓慢。此外，如果您的数据在其他 Amazon Redshift 数据库表中已经存在，请使用 INSERT INTO ... SELECT FROM 或 CREATE TABLE AS 来提高性能。有关更多信息，请参阅 [INSERT](r_INSERT_30.md) 或 [CREATE TABLE AS](r_CREATE_TABLE_AS.md)。

如果您插入、更新或删除了表中的大量行（相对于更改前的行数），请在完成后对表运行 ANALYZE 和 VACUUM 命令。如果在经过一段时间之后您的应用程序中累积了大量小更改，则可能需要安排定期运行 ANALYZE 和 VACUUM 命令。有关更多信息，请参阅[分析表](t_Analyzing_tables.md)和[对表执行 vacuum 操作](t_Reclaiming_storage_space202.md)。

**Topics**
+ [更新和插入新数据](t_updating-inserting-using-staging-tables-.md)

# 更新和插入新数据
<a name="t_updating-inserting-using-staging-tables-"></a>

您可以使用 MERGE 命令高效地向现有表中添加新数据。执行合并操作，方法是创建暂存表，然后使用本节中描述的方法之一从暂存表更新目标表。有关 MERGE 命令的更多信息，请参阅[MERGE](r_MERGE.md)。

[合并示例](merge-examples.md)使用名为 TICKIT 数据集的 Amazon Redshift 示例数据集。作为先决条件，您可以按照[开始使用常见数据库任务](https://docs.aws.amazon.com/redshift/latest/gsg/database-tasks.html)中提供的说明设置 TICKIT 表和数据。有关示例数据集的更多详细信息可在[示例数据库](https://docs.aws.amazon.com/redshift/latest/dg/c_sampledb.html)中找到。

## 合并方法 1：替换现有行
<a name="merge-method-replace-existing-rows"></a>

如果您要覆盖目标表中的所有列，执行合并的最快方法是替换现有行。这将通过使用内部联接删除将要更新的行，从而仅需扫描目标表一次。在删除某些行后，通过从暂存表执行单个插入操作即可使用新行来替换它们。

请在满足以下所有条件时用此方法：
+ 您的目标表和暂存表包含相同的列。
+ 您要将目标表列中的所有数据替换为所有的暂存表列。
+ 您将在合并操作中使用暂存表中的所有行。

如果未满足以上任一条件，请使用下一节所述的“合并方法 2：在不使用 MERGE 的情况下指定列列表”。

如果您不使用暂存表中的所有行，则使用 WHERE 子句筛选 DELETE 和 INSERT 语句，以忽略未更改的行。但是，如果暂存表中的大多数行不会参与合并，我们建议通过单独的步骤中执行 UPDATE 和 INSERT，如本节后面所述。

## 合并方法 2：在不使用 MERGE 的情况下指定列列表
<a name="merge-method-specify-column-list"></a>

使用此方法可更新目标表中的特定列，而不是覆盖所有行。此方法比前一个方法消耗的时间更长，因为它需要执行一个额外的更新步骤，并且不使用 MERGE 命令。请在满足以下所有条件时用此方法：
+ 并非要更新目标表中的所有列。
+ 暂存表中的大多数行在更新中将不会使用。

**Topics**
+ [合并方法 1：替换现有行](#merge-method-replace-existing-rows)
+ [合并方法 2：在不使用 MERGE 的情况下指定列列表](#merge-method-specify-column-list)
+ [创建临时的暂存表](merge-create-staging-table.md)
+ [替换现有行以执行合并操作](merge-replacing-existing-rows.md)
+ [通过在不使用 MERGE 的情况下指定列列表执行合并操作](merge-specify-a-column-list.md)
+ [合并示例](merge-examples.md)

# 创建临时的暂存表
<a name="merge-create-staging-table"></a>

*暂存表* 是一个临时表，用来保存将用于对*目标表* 进行更改（包括更新和插入）的所有数据。

合并操作需要在暂存表和目标表之间建立联接。要并置联接行，请将暂存表的分配键的列设置为与目标表的分配键的列相同。例如，如果目标表使用一个外键列作为其分配键，则对暂存表的分配键使用相同的列。如果使用 [CREATE TABLE LIKE](r_CREATE_TABLE_NEW.md#create-table-like) 语句创建一个暂存表，则该暂存表将从父表继承分配键。如果使用 CREATE TABLE AS 语句，则新表不会继承分配键。有关更多信息，请参阅[用于优化查询的数据分配](t_Distributing_data.md)。

如果分配键与主键不相同且在合并操作中未更新分配键，则在分配键列上添加一个冗余联接谓词以启用并置联接。例如：

```
where target.primarykey = stage.primarykey 
and target.distkey = stage.distkey
```

要验证查询是否将使用并置联接，请使用 [EXPLAIN](r_EXPLAIN.md) 运行查询并检查所有联接上是否有 DS\$1DIST\$1NONE。有关更多信息，请参阅[评估查询计划](c_data_redistribution.md)。

# 替换现有行以执行合并操作
<a name="merge-replacing-existing-rows"></a>

当您运行此过程中详述的合并操作时，请将所有步骤（但创建和删除临时暂存表除外）放在单个事务中。如果任何步骤失败，事务将回滚。使用单个事务还将减少提交次数，从而节省时间和资源。

**通过替换现有行执行合并操作**

1. 创建暂存表，然后使用要合并的数据填充它，如下面的伪代码所示。

   ```
   CREATE temp table stage (like target); 
   
   INSERT INTO stage 
   SELECT * FROM source 
   WHERE source.filter = 'filter_expression';
   ```

1.  使用 MERGE 执行与暂存表的内部联接，以更新目标表中与暂存表匹配的行，然后将所有与暂存表不匹配的剩余行插入到目标表中。

    我们建议您在单个 MERGE 命令中运行更新和插入操作。

   ```
   MERGE INTO target 
   USING stage [optional alias] on (target.primary_key = stage.primary_key)
   WHEN MATCHED THEN 
   UPDATE SET col_name1 = stage.col_name1 , col_name2= stage.col_name2, col_name3 = {expr}
   WHEN NOT MATCHED THEN
   INSERT (col_name1 , col_name2, col_name3) VALUES (stage.col_name1, stage.col_name2, {expr});
   ```

1. 删除暂存表。

   ```
   DROP TABLE stage;
   ```

# 通过在不使用 MERGE 的情况下指定列列表执行合并操作
<a name="merge-specify-a-column-list"></a>

当您运行此过程中详述的合并操作时，请将所有步骤放在单个事务中。如果任何步骤失败，事务将回滚。使用单个事务还将减少提交次数，从而节省时间和资源。

**通过指定列列表执行合并操作**

1. 将整个操作放在单个事务块中。

   ```
   BEGIN transaction;
   … 
   END transaction;
   ```

1. 创建暂存表，然后使用要合并的数据填充它，如下面的伪代码所示。

   ```
   create temp table stage (like target); 
   insert into stage 
   select * from source 
   where source.filter = 'filter_expression';
   ```

1. 使用与暂存表的内部联接更新目标表。
   + 在 UPDATE 子句中，显式列出要更新的列。
   + 执行与暂存表的内部联接。
   + 如果分配键与主键不同且分配键将不会更新，请在分配键上添加一个冗余联接。要验证查询是否将使用并置联接，请使用 [EXPLAIN](r_EXPLAIN.md) 运行查询并检查所有联接上是否有 DS\$1DIST\$1NONE。有关更多信息，请参阅[评估查询计划](c_data_redistribution.md)。
   + 如果目标表按时间戳排序，请添加谓词以对目标表使用限定范围的扫描。有关更多信息，请参阅 [设计查询的 Amazon Redshift 最佳实践](c_designing-queries-best-practices.md)。
   + 如果您将不在合并中使用所有行，请添加一个子句以筛选要更改的行。例如，添加针对一个或多个列的不等于筛选器以排除未更改的行。
   + 将更新、删除和插入操作放在单个事务块中，以便在出现问题时回滚所有内容。

    例如：

   ```
   begin transaction;
   
   update target 
   set col1 = stage.col1, 
   col2 = stage.col2, 
   col3 = 'expression' 
   from stage 
   where target.primarykey = stage.primarykey 
   and target.distkey = stage.distkey 
   and target.col3 > 'last_update_time' 
   and (target.col1 != stage.col1 
   or target.col2 != stage.col2 
   or target.col3 = 'filter_expression');
   ```

1. 使用与目标表的内部联接从暂存表中删除不需要的行。目标表中的一部分行已经与暂存表中的对应行匹配，而另一些行已在上一个步骤中更新。在任一情况下，都不需要插入它们。

   ```
   delete from stage 
   using target 
   where stage.primarykey = target.primarykey;
   ```

1. 插入暂存表中的剩余行。在 VALUES 子句中使用您在步骤 2 的 UPDATE 语句中使用的同一列列表。

   ```
   insert into target
   (select col1, col2, 'expression'
   from stage);
   
   end transaction;
   ```

1. 删除暂存表。

   ```
   drop table stage;
   ```

# 合并示例
<a name="merge-examples"></a>

以下示例将执行一个合并以更新 SALES 表。第一个示例使用了较简单的方法：从目标表中删除所有行，然后插入暂存表中的所有行。第二个示例需要更新目标表中的选定列，因此它包含一个额外的更新步骤。

[合并示例](#merge-examples)使用名为 TICKIT 数据集的 Amazon Redshift 示例数据集。作为先决条件，您可以按照[开始使用常见数据库任务](https://docs.aws.amazon.com/redshift/latest/gsg/database-tasks.html)指南中提供的说明设置 TICKIT 表和数据。有关示例数据集的更多详细信息可在[示例数据库](https://docs.aws.amazon.com/redshift/latest/dg/c_sampledb.html)中找到。

**合并数据来源示例**

本节中的示例需要同时包含更新和插入的样本数据来源。例如，我们将创建一个名为 SALES\$1UPDATE 的示例表，该表使用 SALES 表中的数据。我们将使用表示 12 月的新销售活动的随机数据填充新表。我们将使用 SALES\$1UPDATE 示例表在后面的示例中创建暂存表。

```
-- Create a sample table as a copy of the SALES table.

create table tickit.sales_update as
select * from tickit.sales;

-- Change every fifth row to have updates.

update tickit.sales_update
set qtysold = qtysold*2,
pricepaid = pricepaid*0.8,
commission = commission*1.1
where saletime > '2008-11-30'
and mod(sellerid, 5) = 0;

-- Add some new rows to have inserts.
-- This example creates a duplicate of every fourth row.

insert into tickit.sales_update
select (salesid + 172456) as salesid, listid, sellerid, buyerid, eventid, dateid, qtysold, pricepaid, commission, getdate() as saletime
from tickit.sales_update
where saletime > '2008-11-30'
and mod(sellerid, 4) = 0;
```

**基于匹配键替换现有行的合并示例**

以下脚本使用 SALES\$1UPDATE 表对包含 12 月销售活动的新数据的 SALES 表执行合并操作。此示例替换 SALES 表中具有更新的行。对于此示例，我们将更新 qtysold 和 pricepaid 列，但让 commission 和 saletime 保持不变。

```
MERGE into tickit.sales 
USING tickit.sales_update sales_update  
on ( sales.salesid = sales_update.salesid
and sales.listid = sales_update.listid
and sales_update.saletime > '2008-11-30'
and (sales.qtysold != sales_update.qtysold 
or sales.pricepaid != sales_update.pricepaid))
WHEN MATCHED THEN
update SET qtysold = sales_update.qtysold,
pricepaid = sales_update.pricepaid
WHEN NOT MATCHED THEN 
INSERT (salesid, listid, sellerid, buyerid, eventid, dateid, qtysold , pricepaid, commission, saletime)
values (sales_update.salesid, sales_update.listid, sales_update.sellerid, sales_update.buyerid, sales_update.eventid, 
sales_update.dateid, sales_update.qtysold , sales_update.pricepaid, sales_update.commission, sales_update.saletime);

-- Drop the staging table.
drop table tickit.sales_update;

-- Test to see that commission and salestime were not impacted.
SELECT sales.salesid, sales.commission, sales.salestime, sales_update.commission, sales_update.salestime 
FROM tickit.sales 
INNER JOIN tickit.sales_update sales_update  
ON 
sales.salesid = sales_update.salesid
AND sales.listid = sales_update.listid
AND sales_update.saletime > '2008-11-30'
AND (sales.commission != sales_update.commission 
OR sales.salestime != sales_update.salestime);
```

**在不使用 MERGE 的情况下指定列列表的合并示例**

以下示例将执行合并操作以使用 12 月销售活动的新数据更新 SALES。我们需要同时包含更新和插入的样本数据，以及未更改的行。在此示例中，我们希望更新 QTYSOLD 和 PRICEPAID 列，但让 COMMISSION 和 SALETIME 保持不变。以下脚本使用 SALES\$1UPDATE 表来对 SALES 表执行合并操作。

```
-- Create a staging table and populate it with rows from SALES_UPDATE for Dec
create temp table stagesales as select * from sales_update
where saletime > '2008-11-30';

-- Start a new transaction
begin transaction;

-- Update the target table using an inner join with the staging table
-- The join includes a redundant predicate to collocate on the distribution key –- A filter on saletime enables a range-restricted scan on SALES

update sales
set qtysold = stagesales.qtysold,
pricepaid = stagesales.pricepaid
from stagesales
where sales.salesid = stagesales.salesid
and sales.listid = stagesales.listid
and stagesales.saletime > '2008-11-30'
and (sales.qtysold != stagesales.qtysold 
or sales.pricepaid != stagesales.pricepaid);
 
-- Delete matching rows from the staging table 
-- using an inner join with the target table

delete from stagesales
using sales
where sales.salesid = stagesales.salesid
and sales.listid = stagesales.listid;

-- Insert the remaining rows from the staging table into the target table
insert into sales
select * from stagesales;

-- End transaction and commit
end transaction;

-- Drop the staging table
drop table stagesales;
```

# 执行深层复制
<a name="performing-a-deep-copy"></a>

深层复制将使用批量插入重新创建和重新填充表，这将自动对表进行排序。如果表具有大型未排序区域，则深层复制比 vacuum 快得多。如果您可以跟踪并发更新，我们建议您仅在深层复制操作期间进行并发更新。该过程完成后，将增量更新移到新表中。VACUUM 操作支持自动并发更新。

您可以选择四种方法之一来创建原始表的副本：
+ 使用原始表 DDL。

  如果 CREATE TABLE DDL 可用，那么这是最快且首选的方法。在创建新表时，您可以指定所有表和列属性，包括主键和外键。您可以使用 SHOW TABLE 函数找到原始 DDL。
+ 使用 CREATE TABLE LIKE。

  如果原始 DDL 不可用，您可以使用 CREATE TABLE LIKE 来重新创建原始表。新表继承父表的编码、分配键、排序键和 not-null 属性。新表不继承父表的主键和外键属性，但您可以使用 [ALTER TABLE](r_ALTER_TABLE.md) 来添加它们。
+ 创建一个临时表并截断原始表。

  如果您必须保留父表的主键和外键属性。如果父表有依赖关系，您可以使用 CREATE TABLE ... AS (CTAS) 以创建临时表。然后，截断原始表并从临时表填充它。

  与使用永久表相比，使用临时表可极大地提高性能，但存在丢失数据的风险。在创建临时表的会话结束时将自动删除该临时表。TRUNCATE 将立即提交，即使它在事务块中。如果 TRUNCATE 成功，但会话在接下来的 INSERT 完成前关闭，则数据将丢失。如果数据丢失是不可接受的，请使用永久表。

创建表的副本后，您可能必须授予对新表的访问权限。您可以使用 [GRANT](r_GRANT.md) 来定义访问权限。要查看和授予表的所有访问权限，您必须是以下人员之一：
+  超级用户。
+  您想复制的表的拥有者。
+  拥有 ACCESS SYSTEM TABLE 权限以查看表的权限且具有所有相关权限的授予权限的用户。

此外，您可能必须授予对于深层复制所在模式的使用权限。如果您的深层复制的模式与原始表的模式不同，也不是 `public` 模式，则授予使用权限是必需的。要查看和授予使用权限，您必须是以下人员之一：
+  超级用户。
+  可以授予对深层复制模式的 USAGE 权限的用户。

**使用原始表 DDL 执行深层复制**

1. （可选）通过运行名为 `v_generate_tbl_ddl` 的脚本来重新创建表 DDL。

1. 使用原始 CREATE TABLE DDL 创建表的副本。

1. 使用 INSERT INTO … SELECT 语句向副本填充原始表中的数据。

1. 检查所授予的对于旧表的权限。您可以在 SVV\$1RELATION\$1PRIVILEGES 系统视图中查看这些权限。

1. 如有必要，将旧表的权限授予新表。

1. 向在原始表中具有权限的每个组和用户授予使用权限。如果您的深层复制表处于 `public` 模式，或者与原始表处于同一模式，则无需执行此步骤。

1. 删除原始表。

1. 使用 ALTER TABLE 语句将副本重命名为原始表名称。

以下示例使用名为 sample\$1copy 的 SAMPLE 的副本对 SAMPLE 表执行深层复制。

```
--Create a copy of the original table in the sample_namespace namespace using the original CREATE TABLE DDL.
create table sample_namespace.sample_copy ( … );

--Populate the copy with data from the original table in the public namespace.
insert into sample_namespace.sample_copy (select * from public.sample);

--Check SVV_RELATION_PRIVILEGES for the original table's privileges.
select * from svv_relation_privileges where namespace_name = 'public' and relation_name = 'sample' order by identity_type, identity_id, privilege_type;

--Grant the original table's privileges to the copy table.
grant DELETE on table sample_namespace.sample_copy to group group1;
grant INSERT, UPDATE on table sample_namespace.sample_copy to group group2;
grant SELECT on table sample_namespace.sample_copy to user1;
grant INSERT, SELECT, UPDATE on table sample_namespace.sample_copy to user2;
         
--Grant usage permission to every group and user that has privileges in the original table.
grant USAGE on schema sample_namespace to group group1, group group2, user1, user2;

--Drop the original table.
drop table public.sample;

--Rename the copy table to match the original table's name.
alter table sample_namespace.sample_copy rename to sample;
```

**使用 CREATE TABLE LIKE 执行深层复制**

1. 使用 CREATE TABLE LIKE 创建新表。

1. 使用 INSERT INTO … SELECT 语句将当前表中的行复制到新表。

1. 检查所授予的对于旧表的权限。您可以在 SVV\$1RELATION\$1PRIVILEGES 系统视图中查看这些权限。

1. 如有必要，将旧表的权限授予新表。

1. 向在原始表中具有权限的每个组和用户授予使用权限。如果您的深层复制表处于 `public` 模式，或者与原始表处于同一模式，则无需执行此步骤。

1. 删除当前表。

1. 使用 ALTER TABLE 语句将新表重命名为原始表名称。

以下示例使用 CREATE TABLE LIKE 对 SAMPLE 表执行深层复制。

```
--Create a copy of the original table in the sample_namespace namespace using CREATE TABLE LIKE.
create table sameple_namespace.sample_copy (like public.sample);

--Populate the copy with data from the original table.
insert into sample_namespace.sample_copy (select * from public.sample);

--Check SVV_RELATION_PRIVILEGES for the original table's privileges.
select * from svv_relation_privileges where namespace_name = 'public' and relation_name = 'sample' order by identity_type, identity_id, privilege_type;

--Grant the original table's privileges to the copy table.
grant DELETE on table sample_namespace.sample_copy to group group1;
grant INSERT, UPDATE on table sample_namespace.sample_copy to group group2;
grant SELECT on table sample_namespace.sample_copy to user1;
grant INSERT, SELECT, UPDATE on table sample_namespace.sample_copy to user2;
         
--Grant usage permission to every group and user that has privileges in the original table.
grant USAGE on schema sample_namespace to group group1, group group2, user1, user2;

--Drop the original table.
drop table public.sample;

--Rename the copy table to match the original table's name.
alter table sample_namespace.sample_copy rename to sample;
```

**通过创建临时表并截断原始表来执行深层复制**

1. 使用 CREATE TABLE AS 创建具有原始表中的行的临时表。

1. 截断当前表。

1. 使用 INSERT INTO … SELECT 语句将临时表中的行复制到原始表。

1. 删除临时表。

以下示例通过创建临时表并截断原始表来对 SALES 表执行深层复制。由于原始表仍然存在，因此您无需授予对副本表的权限。

```
--Create a temp table copy using CREATE TABLE AS.
create temp table salestemp as select * from sales;

--Truncate the original table.
truncate sales;

--Copy the rows from the temporary table to the original table.
insert into sales (select * from salestemp);

--Drop the temporary table.
drop table salestemp;
```

# 分析表
<a name="t_Analyzing_tables"></a>

ANALYZE 操作更新查询计划程序用来选择最佳计划的统计元数据。

在大多数情况下，您无需显式运行 ANALYZE 命令。Amazon Redshift 监控您工作负载的更改，并在后台自动更新统计数据。此外，COPY 命令会在将数据加载到空表时自动执行分析。

要明确分析表或整个数据库，请运行 [ANALYZE](r_ANALYZE.md) 命令。

## 自动分析
<a name="t_Analyzing_tables-auto-analyze"></a>

Amazon Redshift 持续监控您的数据库，并自动在后台执行分析操作。为了最大限度地降低对系统性能的影响，自动分析将在工作负载较轻的时段运行。

默认情况下会启用自动分析。要关闭自动分析，请通过修改集群的参数组来将 `auto_analyze` 参数设置为 **false**。

为了减少处理时间并提高整体系统性能，Amazon Redshift 将跳过对任何修改程度较小的表的自动分析。

分析操作将跳过具有最新统计数据的表。如果您将 ANALYZE 作为提取、转换和加载 (ETL) 工作流的一部分运行，则自动分析将跳过具有最新统计数据的表。类似地，在自动分析更新表的统计数据后，显式 ANALYZE 将跳过表。

## 分析新表数据
<a name="t_Analyzing_tables-new-tables"></a>

 默认情况下，COPY 命令会在将数据加载到空表后执行 ANALYZE。无论表是否为空，您都可以通过设置 STATUPDATE ON 来强制执行 ANALYZE。如果您指定 STATUPDATE OFF，则不会执行 ANALYZE。仅表所有者或超级用户才可以运行 ANALYZE 命令，或在 STATUPDATE 设置为 ON 时运行 COPY 命令。

Amazon Redshift 还分析您使用以下命令创建的新表：
+ CREATE TABLE AS (CTAS) 
+ CREATE TEMP TABLE AS 
+ SELECT INTO 

当您对最初加载其数据后未分析的新表运行查询时，Amazon Redshift 将返回一条警告消息。在后续更新或加载后查询表时，不会出现警告。在您对引用未经分析的表的查询运行 EXPLAIN 命令时，将返回相同的警告消息。

在通过将数据添加到非空表来明显更改表的大小时，您可以明确更新统计数据。可以通过运行 ANALYZE 命令或将 STATUPDATE ON 选项用于 COPY 命令来做到这一点。要查看有关自上次执行 ANALYZE 以来插入或删除的行数的详细信息，请查询 [PG\$1STATISTIC\$1INDICATOR](r_PG_STATISTIC_INDICATOR.md) 系统目录表。

您可以将 [ANALYZE](r_ANALYZE.md) 命令的范围指定为下列选项之一：
+ 整个当前数据库
+ 单个表
+ 单个表中的一个或多个特定列
+ 有可能在查询中用作谓词的列

 ANALYZE 命令将从表中获取行的采样，执行一些计算，并保存生成的列统计数据。预设情况下，Amazon Redshift 将为 DISTKEY 列运行一个采样过程，并为表中所有其他列运行另一个采样过程。如果您希望为一部分列生成统计数据，则可指定一个逗号分隔的列列表。您可以将 ANALYZE 与 PREDICATE COLUMNS 子句一起运行来跳过用作谓词的列。

 ANALYZE 操作是资源密集型的，因此仅对实际需要统计数据更新的表和列运行此类操作。您无需定期或按相同的计划分析所有表中的所有列。如果数据发生重大更改，请分析在以下操作中常用的列：
+ 排序和分组操作
+ 联接
+ 查询谓词

为了减少处理时间并提高整体系统性能，对于具有较低的更改行数百分比（由 [analyze\$1threshold\$1percent](r_analyze_threshold_percent.md) 参数决定）的任何表，Amazon Redshift 将跳过 ANALYZE。默认情况下，分析阈值将设置为 10%。可以通过运行 [SET](r_SET.md) 命令来更改当前会话的分析阈值。

不太可能需要频繁分析的列是表示从未被实际查询的事实和度量以及任何相关属性的列（例如，大量 VARCHAR 列）。例如，请考虑 TICKIT 数据库中的 LISTING 表。

```
select "column", type, encoding, distkey, sortkey
from pg_table_def where tablename = 'listing';


column         |        type        | encoding | distkey | sortkey 
---------------+--------------------+----------+---------+---------
listid         | integer            | none     | t       | 1       
sellerid       | integer            | none     | f       | 0       
eventid        | integer            | mostly16 | f       | 0       
dateid         | smallint           | none     | f       | 0       
numtickets     | smallint           | mostly8  | f       | 0       
priceperticket | numeric(8,2)       | bytedict | f       | 0       
totalprice     | numeric(8,2)       | mostly32 | f       | 0       
listtime       | timestamp with...  | none     | f       | 0
```

如果此表每天加载了大量新记录，则必须定期分析查询中频繁用作联接键的 LISTID 列。如果 TOTALPRICE 和 LISTTIME 是查询中的常用约束，则可在每个工作日分析这些列和分配键。

```
analyze listing(listid, totalprice, listtime);
```

假设应用程序中的卖家和事件变动小得多，且日期 ID 引用仅涵盖两年或三年的一组固定天数。在此情况下，这些列的唯一值将不会发生明显更改。但是，每个唯一值的实例数将平稳增加。

此外，请考虑查询 NUMTICKETS 和 PRICEPERTICKET 度量的频率低于查询 TOTALPRICE 列的频率这种情况。在这种情况下，您可在每个周末对整个表运行一次 ANALYZE 命令，以便更新未每日分析的 5 个列的统计数据：
<a name="t_Analyzing_tables-predicate-columns"></a>
**谓词列**  
作为指定列列表的便利替代方法，您可以选择仅分析可能用作谓词的列。当您运行查询时，在联接、筛选条件或 group by 子句中使用的任意列将在系统目录中标记为谓词列。当您使用 PREDICATE COLUMNS 子句运行 ANALYZE 时，分析操作仅包括满足以下标准的列：
+ 标记为谓词列的列。
+ 该列为分配键。
+ 该列为排序键的一部分。

如果未将任何表的列标记为谓词，则即使指定了 PREDICATE COLUMNS，ANALYZE 仍将包括所有列。如果未将任何列标记为谓词列，这可能是因为尚未查询表。

在工作负载的查询模式相对稳定时，您可以选择使用 PREDICATE COLUMNS。当查询模式可变并且不同的列频繁用作谓词时，使用 PREDICATE COLUMNS 可能会临时得到过时的统计数据。过时的统计数据可能导致查询运行时间计划不够理想和运行时间较长。不过，当您下次使用 PREDICATE COLUMNS 运行 ANALYZE 时，将包括新的谓词列。

要查看谓词列的详细信息，请使用以下 SQL 创建名为 PREDICATE\$1COLUMNS 的视图。

```
CREATE VIEW predicate_columns AS
WITH predicate_column_info as (
SELECT ns.nspname AS schema_name, c.relname AS table_name, a.attnum as col_num,  a.attname as col_name,
        CASE
            WHEN 10002 = s.stakind1 THEN array_to_string(stavalues1, '||') 
            WHEN 10002 = s.stakind2 THEN array_to_string(stavalues2, '||')
            WHEN 10002 = s.stakind3 THEN array_to_string(stavalues3, '||')
            WHEN 10002 = s.stakind4 THEN array_to_string(stavalues4, '||')
            ELSE NULL::varchar
        END AS pred_ts
   FROM pg_statistic s
   JOIN pg_class c ON c.oid = s.starelid
   JOIN pg_namespace ns ON c.relnamespace = ns.oid
   JOIN pg_attribute a ON c.oid = a.attrelid AND a.attnum = s.staattnum)
SELECT schema_name, table_name, col_num, col_name,
       pred_ts NOT LIKE '2000-01-01%' AS is_predicate,
       CASE WHEN pred_ts NOT LIKE '2000-01-01%' THEN (split_part(pred_ts, '||',1))::timestamp ELSE NULL::timestamp END as first_predicate_use,
       CASE WHEN pred_ts NOT LIKE '%||2000-01-01%' THEN (split_part(pred_ts, '||',2))::timestamp ELSE NULL::timestamp END as last_analyze
FROM predicate_column_info;
```

假设您对 LISTING 表运行以下查询。请注意，LISTID、LISTTIME 和 EVENTID 均用于联接、筛选和 group by 子句中。

```
select s.buyerid,l.eventid, sum(l.totalprice)
from listing l
join sales s on l.listid = s.listid
where l.listtime > '2008-12-01'
group by l.eventid, s.buyerid;
```

当您查询 PREDICATE\$1COLUMNS 视图时，如下例中所示，可以看到 LISTID、EVENTID 和 LISTTIME 标记为谓词列。

```
select * from predicate_columns 
where table_name = 'listing';
```

```
schema_name | table_name | col_num | col_name       | is_predicate | first_predicate_use | last_analyze       
------------+------------+---------+----------------+--------------+---------------------+--------------------
public      | listing    |       1 | listid         | true         | 2017-05-05 19:27:59 | 2017-05-03 18:27:41
public      | listing    |       2 | sellerid       | false        |                     | 2017-05-03 18:27:41
public      | listing    |       3 | eventid        | true         | 2017-05-16 20:54:32 | 2017-05-03 18:27:41
public      | listing    |       4 | dateid         | false        |                     | 2017-05-03 18:27:41
public      | listing    |       5 | numtickets     | false        |                     | 2017-05-03 18:27:41
public      | listing    |       6 | priceperticket | false        |                     | 2017-05-03 18:27:41
public      | listing    |       7 | totalprice     | false        |                     | 2017-05-03 18:27:41
public      | listing    |       8 | listtime       | true         | 2017-05-16 20:54:32 | 2017-05-03 18:27:41
```

通过允许查询计划程序选择最佳计划，使统计数据保持最新可提高查询性能。Amazon Redshift 在后台自动刷新统计数据，您也可以明确运行 ANALYZE 命令。如果您选择明确运行 ANALYZE，请执行以下操作：
+ 在运行查询之前运行 ANALYZE 命令。
+ 在每次定期加载或更新循环结束时，常规性地对数据库运行 ANALYZE 命令。
+ 对您创建的任何新表和正在进行重大更改的任何现有表或列运行 ANALYZE 命令。
+ 考虑按不同计划对不同类型的表和列运行 ANALYZE 操作，具体取决于它们在查询中的使用和它们的更改倾向。
+ 为节省时间和集群资源，在运行 ANALYZE 时请使用 PREDICATE COLUMNS 子句。

您不必在将快照还原到预置集群或无服务器命名空间之后显式运行 ANALYZE 命令，也不必在恢复已暂停的预置集群之后显式运行此命令。在这些情况下，Amazon Redshift 会保留系统表信息，从而无需手动执行 ANALYZE 命令。Amazon Redshift 将继续根据需要运行自动分析操作。

分析操作将跳过具有最新统计数据的表。如果您将 ANALYZE 作为提取、转换和加载 (ETL) 工作流的一部分运行，则自动分析将跳过具有最新统计数据的表。类似地，在自动分析更新表的统计数据后，显式 ANALYZE 将跳过表。

## ANALYZE 命令历史记录
<a name="c_check_last_analyze"></a>

了解上次对表或数据库运行 ANALYZE 命令的时间很有用。运行 ANALYZE 命令时，Amazon Redshift 将运行与以下内容类似的多个查询：

```
padb_fetch_sample: select * from table_name
```

查询 STL\$1ANALYZE 以查看分析操作的历史记录。如果 Amazon Redshift 使用自动分析功能来分析表，则 `is_background` 列将设置为 `t`（真）。否则，它将设置为 `f`（假）。以下示例联接 STV\$1TBL\$1PERM 以显示表名称和运行时间详细信息。

```
select distinct a.xid, trim(t.name) as name, a.status, a.rows, a.modified_rows, a.starttime, a.endtime
from stl_analyze a 
join stv_tbl_perm t  on t.id=a.table_id
where name = 'users'
order by starttime;


xid    | name  | status          | rows  | modified_rows | starttime           | endtime            
-------+-------+-----------------+-------+---------------+---------------------+--------------------
  1582 | users | Full            | 49990 |         49990 | 2016-09-22 22:02:23 | 2016-09-22 22:02:28
244287 | users | Full            | 24992 |         74988 | 2016-10-04 22:50:58 | 2016-10-04 22:51:01
244712 | users | Full            | 49984 |         24992 | 2016-10-04 22:56:07 | 2016-10-04 22:56:07
245071 | users | Skipped         | 49984 |             0 | 2016-10-04 22:58:17 | 2016-10-04 22:58:17
245439 | users | Skipped         | 49984 |          1982 | 2016-10-04 23:00:13 | 2016-10-04 23:00:13
(5 rows)
```

或者，您可以运行一个更复杂的查询，该查询将返回在每个包括 ANALYZE 命令的已完成事务中运行的所有语句：

```
select xid, to_char(starttime, 'HH24:MM:SS.MS') as starttime,
datediff(sec,starttime,endtime ) as secs, substring(text, 1, 40)
from svl_statementtext
where sequence = 0
and xid in (select xid from svl_statementtext s where s.text like 'padb_fetch_sample%' )
order by xid desc, starttime;

xid  |  starttime   | secs |                  substring
-----+--------------+------+------------------------------------------
1338 | 12:04:28.511 |    4 | Analyze date
1338 | 12:04:28.511 |    1 | padb_fetch_sample: select count(*) from
1338 | 12:04:29.443 |    2 | padb_fetch_sample: select * from date
1338 | 12:04:31.456 |    1 | padb_fetch_sample: select * from date
1337 | 12:04:24.388 |    1 | padb_fetch_sample: select count(*) from
1337 | 12:04:24.388 |    4 | Analyze sales
1337 | 12:04:25.322 |    2 | padb_fetch_sample: select * from sales
1337 | 12:04:27.363 |    1 | padb_fetch_sample: select * from sales
...
```

# 对表执行 vacuum 操作
<a name="t_Reclaiming_storage_space202"></a>

Amazon Redshift 可以在后台自动对表进行排序并执行 VACUUM DELETE 操作。要在加载或一系列增量更新操作后清理表，您也可以对整个数据库或对单个表运行 [VACUUM](r_VACUUM_command.md) 命令。

**注意**  
只有拥有必要的表权限的用户才能有效地对表执行 vacuum 操作。如果在没有必需的表权限的情况下运行 VACUUM 操作，该操作将成功完成，但不起任何作用。有关能有效运行 VACUUM 操作的有效表权限列表，请参阅[VACUUM](r_VACUUM_command.md)。  
出于此原因，我们建议根据需要对各个表执行 vacuum 操作。我们也推荐此方法，因为对整个数据库进行 vacuum 操作可能会消耗大量资源。

## 自动表排序
<a name="automatic-table-sort"></a>

Amazon Redshift 在后台自动对数据进行排序以按照其排序键顺序保留表数据。Amazon Redshift 将跟踪您的扫描查询以确定表的哪些部分将从排序中受益。Amazon Redshift 还会跟踪来自并发扩展集群的扫描查询。对于使用 Amazon Redshift 数据共享的多集群架构，Amazon Redshift 还会跟踪来自数据网格中使用者集群/工作组（包括不同区域的集群/工作组）的扫描查询。系统将来自主集群、并发扩展集群和使用者集群中的扫描统计数据聚合在一起，来确定表的哪些部分可以从排序中获益。

根据系统上的负载，Amazon Redshift 自动启动排序操作。此自动排序减少了运行 VACUUM 命令以按排序键顺序保留数据的需求。如果您需要按排序键顺序对数据进行完全排序（例如，在加载大量数据之后），则您仍可以手动运行 VACUUM 命令。要通过运行 VACUUM SORT 来确定您的表是否将受益，请监控 `vacuum_sort_benefit` 中的 [SVV\$1TABLE\$1INFO](r_SVV_TABLE_INFO.md) 列。

Amazon Redshift 跟踪在每个表上使用排序键的扫描查询。Amazon Redshift 估计每个表（如果表已完全排序）在数据扫描和筛选方面的最大改进百分比。此估计值在 `vacuum_sort_benefit` 中的 [SVV\$1TABLE\$1INFO](r_SVV_TABLE_INFO.md) 列中可见。您可以将此列与 `unsorted` 列结合使用，确定查询何时可以从手动对表运行 VACUUM SORT 中受益。`unsorted` 列反映表的物理排序顺序。`vacuum_sort_benefit` 列指定通过手动运行 VACUUM SORT 对表进行排序的影响。

例如，请考虑以下查询：

```
select "table", unsorted,vacuum_sort_benefit from svv_table_info order by 1;
```

```
 table | unsorted | vacuum_sort_benefit 
-------+----------+---------------------
 sales |    85.71 |                5.00
 event |    45.24 |               67.00
```

对于表“sales”，即使该表的物理未排序项约为 86%，其对查询性能的影响也仅为 5%。这可能是因为查询只访问表的一小部分内容，也可能是因为访问表的查询几近于无。对于表“event”，该表的物理未排序项约为 45%。不过，67% 的查询性能影响表明查询访问了表的更大部分内容，或者访问表的查询的数量很多。表“event”可能会从运行 VACUUM SORT 中受益。

## 自动 vacuum 删除
<a name="automatic-table-delete"></a>

执行 delete 操作时，会将行标记为删除，但不会删除。Amazon Redshift 会根据数据库表中已删除的行数在后台自动运行 VACUUM DELETE 操作。Amazon Redshift 安排 VACUUM DELETE 在负载减少期间运行，并在高负载期间暂停操作。

**Topics**
+ [自动表排序](#automatic-table-sort)
+ [自动 vacuum 删除](#automatic-table-delete)
+ [vacuum 频率](#vacuum-frequency)
+ [排序阶段和合并阶段](#vacuum-stages)
+ [vacuum 阈值](#vacuum-sort-threshold)
+ [vacuum 类型](#vacuum-types)
+ [最大程度地减少 vacuum 次数](vacuum-managing-vacuum-times.md)

## vacuum 频率
<a name="vacuum-frequency"></a>

您应按照所需的频率执行 vacuum 操作以保持一致的查询性能。在确定运行 VACUUM 命令的频率时，请考虑以下因素：
+ 在预计集群上的活动最少的时间段（例如，夜晚或指定的数据库管理时段）内运行 VACUUM。
+ 在维护时段之外运行 VACUUM 命令。有关更多信息，请参阅[计划维护时段](https://docs.aws.amazon.com/redshift/latest/dg/c_best-practices-avoid-maintenance.html)。
+ 大型未排序区域将导致更长的 vacuum 时间。如果您延迟 vacuum 操作，则 vacuum 操作将需要更长的时间，因为需要识别更多数据。
+ VACUUM 是 I/O 密集型操作，因此，完成 vacuum 操作所需的时间越长，它对您的集群上运行的并发查询和其他数据库操作的影响就越大。
+ 对于使用交错排序的表，VACUUM 花费的时间更长。要评估是否必须对交错的表重新排序，请查询 [SVV\$1INTERLEAVED\$1COLUMNS](r_SVV_INTERLEAVED_COLUMNS.md) 视图。

## 排序阶段和合并阶段
<a name="vacuum-stages"></a>

Amazon Redshift 按照两个阶段执行 vacuum 操作：首先，它会对未排序区域中的行进行排序；然后，如有必要，会将表结尾处的新排序的行与现有行合并。在对大型表执行 vacuum 操作时，vacuum 操作将在合并后继续一系列步骤（包括增量排序）。如果操作失败，或者如何 Amazon Redshift 在 vacuum 操作期间脱机，已部分执行 vacuum 操作的表或数据库将处于一致状态，但您必须手动重启 vacuum 操作。增量排序将丢失，但不需要再次对失败前已提交的合并行执行 vacuum 操作。如果未排序区域较大，则浪费的时间可能更多。有关排序阶段和合并阶段的更多信息，请参阅[减少合并的行数](vacuum-managing-vacuum-times.md#vacuum-managing-volume-of-unmerged-rows)。

在对表执行 vacuum 操作时，用户可以访问表。您可以在对表执行 vacuum 操作的同时执行查询和写入操作，但如果 DML 和 vacuum 操作同时运行，则二者可能花费更长时间。如果您在 vacuum 操作期间执行 UPDATE 和 DELETE 语句，则系统性能可能会降低。增量合并会临时阻止并发 UPDATE 和 DELETE 操作，而 UPDATE 和 DELETE 反过来会临时阻止对受影响的表执行增量合并步骤。在对表执行完 vacuum 操作前，DDL 操作（例如 ALTER TABLE）将被阻止。

**注意**  
VACUUM 的各种修饰符控制它的工作方式。您可以使用它们来根据当前需求定制 vacuum 操作。例如，使用 VACUUM RECLUSTER 可通过不执行完全合并操作来缩短 vacuum 操作。有关更多信息，请参阅 [VACUUM](r_VACUUM_command.md)。

## vacuum 阈值
<a name="vacuum-sort-threshold"></a>

默认情况下，当任意表中有 95% 的行已有序时，VACUUM 会为该表跳过排序阶段。跳过排序阶段能够显著提高 VACUUM 的性能。要更改某个表的默认排序阈值，请在运行 VACUUM 命令时包含表名称和 TO *threshold* PERCENT 参数。

## vacuum 类型
<a name="vacuum-types"></a>

有关不同 vacuum 类型的信息，请参阅[VACUUM](r_VACUUM_command.md)。

# 最大程度地减少 vacuum 次数
<a name="vacuum-managing-vacuum-times"></a>

 Amazon Redshift 自动对数据进行排序，并在后台运行 VACUUM DELETE。这减少了运行 VACUUM 命令的需要。Vacuum 可能是一个耗时的过程。根据数据的性质，建议您采用以下做法来最大程度地减少 vacuum 次数。

**Topics**
+ [决定是否重建索引](#r_vacuum-decide-whether-to-reindex)
+ [减少未排序区域的大小](#r_vacuum_diskspacereqs)
+ [减少合并的行数](#vacuum-managing-volume-of-unmerged-rows)
+ [按排序键顺序加载数据](#vacuum-load-in-sort-key-order)
+ [使用时间序列表来减少存储的数据](#vacuum-time-series-tables)

## 决定是否重建索引
<a name="r_vacuum-decide-whether-to-reindex"></a>

通常，您可以使用交错排序样式来显著提高查询性能，但是随着时间的推移，如果排序键列中值的分配更改，性能可能会下降。

最初使用 COPY 或 CREATE TABLE AS 加载空交错表时，Amazon Redshift 自动构建交错索引。如果您最初使用 INSERT 加载交错表，则需要在之后运行 VACUUM REINDEX 以初始化交错索引。

随着时间的推移，在添加带有新排序键值的行后，如果排序键列中值的分布发生更改，性能可能会下降。如果新行主要处于现有排序键值的范围内，则不必重建索引。可以运行 VACUUM SORT ONLY 或 VACUUM FULL 恢复排序顺序。

查询引擎能够使用排序顺序高效地选择处理查询所需扫描的数据块。对于交错排序，Amazon Redshift 将分析排序键列值以确定最佳排序顺序。如果键值的分配在添加行时发生更改或偏移，则排序策略将不再是最佳的排序策略，并且排序的性能优势也会减小。要重新分析排序键分配，您可以运行 VACUUM REINDEX。重建索引操作非常耗时，因此，要决定表是否将从重建索引中获益，请查询 [SVV\$1INTERLEAVED\$1COLUMNS](r_SVV_INTERLEAVED_COLUMNS.md) 视图。

例如，以下查询将显示使用交错排序键的表的详细信息。

```
select tbl as tbl_id, stv_tbl_perm.name as table_name, 
col, interleaved_skew, last_reindex
from svv_interleaved_columns, stv_tbl_perm
where svv_interleaved_columns.tbl = stv_tbl_perm.id
and interleaved_skew is not null;


 tbl_id | table_name | col | interleaved_skew | last_reindex
--------+------------+-----+------------------+--------------------
 100048 | customer   |   0 |             3.65 | 2015-04-22 22:05:45
 100068 | lineorder  |   1 |             2.65 | 2015-04-22 22:05:45
 100072 | part       |   0 |             1.65 | 2015-04-22 22:05:45
 100077 | supplier   |   1 |             1.00 | 2015-04-22 22:05:45
(4 rows)
```

`interleaved_skew` 的值是一个比率，指示偏移量。值 1 表示无偏移。如果偏移大于 1.4，VACUUM REINDEX 通常会提高性能，除非偏移是基础集中固有的。

您可以使用 `last_reindex` 中的数据值来确定自上次重建索引以来经历的时间。

## 减少未排序区域的大小
<a name="r_vacuum_diskspacereqs"></a>

在将大量新数据加载到已包含数据的表中时，或在例行维护操作不包含对表进行的 vacuum 操作时，未排序区域将增大。要避免长时间运行的 vacuum 操作，请使用以下做法：
+ 定期运行 vacuum 操作。

  如果您以较小增量加载您的表（例如，表示表中行总数的一小部分的日常更新），定期运行 VACUUM 将帮助确保各个 vacuum 操作快速执行。
+ 首先运行最大加载。

  如果您需要使用多个 COPY 操作加载新表，请首先运行最大加载。当您运行到新的或截断的表中的初始加载时，所有数据将直接加载到已排序区域，因此无需执行 vacuum 操作。
+ 截断表而不是删除所有行。

  从表中删除行不会回收行占用的空间，除非您执行 vacuum 操作；不过，截断表将清空表并回收磁盘空间，因此无需执行 vacuum 操作。或者，请删除表并重新创建它。
+ 截断或删除测试表。

  如果您正在将少量行加载到表中以进行测试，请在完成此操作后不要删除这些行。相反，作为后续生产加载操作的一部分，请截断表并重新加载这些行。
+ 执行深层复制。

  如果使用复合排序键的表具有大型未排序区域，则深层复制要比 vacuum 快得多。深层复制将使用批量插入来重新创建并重新填充表，这将自动对表进行重新排序。如果表拥有大型未排序区域，深层复制将比真空化快得多。这样做的代价是，您不能在深层复制操作过程中进行并行更新，但可以在真空化时这样做。有关更多信息，请参阅 [设计查询的 Amazon Redshift 最佳实践](c_designing-queries-best-practices.md)。

## 减少合并的行数
<a name="vacuum-managing-volume-of-unmerged-rows"></a>

如果 vacuum 操作需要将新行合并到表的已排序区域，vacuum 所需的时间将随表的增大而增多。您可以通过减少必须合并的行数来提高 vacuum 性能。

在执行 vacuum 操作之前，表包含一个已排序区域（位于表开头处），后跟一个未排序区域（当添加或更新行时，该区域将增大）。如果 COPY 操作添加一组行，则这组新行在添加到表结尾处的未排序区域时将基于排序键进行排序。新行将在其自己的集合中进行排序，而不是在未排序区域内进行排序。

下图说明了两次连续 COPY 操作后的未排序区域，其中排序键为 CUSTID。为简便起见，此示例显示一个复合排序键，但相同的原则适用于交错排序键，只不过未排序区域对交错表的影响更大。

![\[一个未排序的表，其中保存着两次 COPY 操作的记录。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/vacuum-unsorted-region.png)


Vacuum 将按两个阶段还原复表的排序顺序：

1. 将未排序区域归入新排序的区域。

   第一个阶段的成本相对较低，因为仅重写未排序区域。如果新排序区域的排序键值的范围大于现有范围，则仅需要重写新行即可完成 vacuum。例如，如果已排序区域包含的 ID 值介于 1 和 500 之间，并且后续复制操作将添加大于 500 的键值，则仅需重写未排序区域。

1. 将新排序的区域与之前排序的区域合并。

   如果新排序的区域中的键与已排序区域中的键重叠，则 VACUUM 需要合并这些行。从新排序区域（最低排序键处）的开头开始，vacuum 会将从之前排序的区域和新排序的区域中的合并行写入一组新数据块。

新排序键范围与现有排序键重叠的程度将决定之前排序的区域需要被重写的程度。如果未排序键遍布于现有排序范围中，则 vacuum 可能需要重写表的现有部分。

下图说明 vacuum 如何对添加到排序键为 CUSTID 的表中的行进行排序和合并。由于每个复制操作将添加一组键值与现有键重叠的新行，因此几乎需要重写整个表。该图显示单一排序和合并，但在实践中，大型 vacuum 包含一系列增量排序和合并步骤。

![\[分两步对示例表进行 VACUUM 操作。首先对新行进行排序，然后将其与现有行合并。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/vacuum-unsorted-region-sort-merge.png)


如果一组新行中的排序键范围与现有键的范围重叠，则合并阶段的成本将继续随表的增大与表大小成比例的增长，而排序阶段的成本与未排序区域的大小成正比。在这种情况下，合并阶段的成本远远超过排序阶段的成本，如下图所示。

![\[图中显示了当新行的排序键与现有行重叠时，合并阶段的成本变得更高。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/vacuum-example-merge-region-grows.png)


要确定已重新合并的表的比例，请在 vacuum 操作完成后查询 SVV\$1VACUUM\$1SUMMARY。以下查询表明，在 CUSTSALES 随时间的推移而增大时，六个连续 vacuum 操作的效果。

```
select * from svv_vacuum_summary
where table_name = 'custsales';


 table_name | xid  | sort_      | merge_     | elapsed_   | row_  | sortedrow_ | block_  | max_merge_
            |      | partitions | increments | time       | delta | delta      | delta   | partitions
 -----------+------+------------+------------+------------+-------+------------+---------+---------------
  custsales | 7072 |          3 |          2 |  143918314 |     0 |   88297472 |   1524  |      47
  custsales | 7122 |          3 |          3 |  164157882 |     0 |   88297472 |    772  |      47
  custsales | 7212 |          3 |          4 |  187433171 |     0 |   88297472 |    767  |      47
  custsales | 7289 |          3 |          4 |  255482945 |     0 |   88297472 |    770  |      47
  custsales | 7420 |          3 |          5 |  316583833 |     0 |   88297472 |    769  |      47
  custsales | 9007 |          3 |          6 |  306685472 |     0 |   88297472 |    772  |      47
 (6 rows)
```

merge\$1increments 列指明了为每个 vacuum 操作合并的数据量。如果连续 vacuum 操作的合并增量的数量按表大小增长的比例增加，它表示每个 vacuum 操作重新合并的表中的行数正在增加，因为现有排序区域和新排序区域重叠。

## 按排序键顺序加载数据
<a name="vacuum-load-in-sort-key-order"></a>

如果您使用 COPY 命令按排序键顺序加载数据，可能会减少甚至消除对 vacuum 的需求。

当满足以下所有条件时，COPY 会向表的有序区域自动添加新行：
+ 表使用了只有一个排序列的复合排序键。
+ 排序列 NOT NULL。
+ 表 100% 有序或为空。
+ 所有新行的排序顺序均优先于现有行，包括标记为要删除的行。在此实例中，Amazon Redshift 使用排序键的前八个字节来确定排序顺序。
+  COPY 命令未触发特定负载优化。加载大量数据时，Amazon Redshift 可能会通过创建新的排序分区（而不是向表的排序区域添加行）来优化性能。

例如，假设您有一个使用客户 ID 和时间记录客户事件的表。如果按客户 ID 进行排序，则增量加载添加的新行的排序键范围可能会与现有范围重叠（如上一个示例中所示），从而导致昂贵的 vacuum 操作。

如果您将排序键设置为时间戳列，新行将按排序顺序追加到表的结尾（如下图所示），从而减少甚至消除对 vacuum 的需求。

![\[使用时间戳列作为排序键的表，获取无需排序的新记录。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/vacuum-unsorted-region-date-sort.png)


## 使用时间序列表来减少存储的数据
<a name="vacuum-time-series-tables"></a>

如果您将数据保留滚动时段，请使用一系列表，如下图所示。

![\[五张表，其中包含五个季度的数据。删除最旧的表以保持一年的滚动时间。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/vacuum-example-unsorted-region-copy-time-series.png)


每当添加一组数据时创建一个新表，然后删除系列中最旧的表。您将获得双重好处：
+ 避免增加删除行的成本，因为 DROP TABLE 操作比大规模 DELETE 更高效。
+ 如果按时间戳对表进行排序，则不需要执行 vacuum 操作。如果每个表包含一个月的数据，则 vacuum 最多必须重写一个月的数据，即使表不是按时间戳排序的也是如此。

您可以创建 UNION ALL 视图供报告查询使用，从而隐藏数据存储在多个表中的事实。如果查询按排序键进行筛选，则查询计划程序可高效地跳过未使用的所有表。UNION ALL 对其他类型的查询可能不太高效，因此您应在所有使用表的查询的环境中评估查询性能。

# 管理并发写入操作
<a name="c_Concurrent_writes"></a>

某些应用程序不仅需要并发查询和加载，还需要能同时对多个表或同一个表进行写入。在此环境中，*并发* 指的是重叠，而不是安排在完全相同的时间运行。如果两个事务中的第二个事务在第一个提交前开始，则将两个事务视为并发。并发操作可源自由同一用户或不同用户控制的不同会话。

Amazon Redshift 通过允许在以递增方式加载或修改表时对表进行读取操作，来支持这些类型的应用程序。查询仅查看数据的最新提交的版本或*快照*，而不是等待提交下一个版本。如果您希望特定查询等待来自另一个写入操作的提交，则必须相应地做出安排。

**注意**  
Amazon Redshift 支持默认的*自动提交*行为，其中，每个单独运行的 SQL 命令都将分别提交。如果您在某个事务数据块中包含一组命令（由 [BEGIN](r_BEGIN.md) 和 [END](r_END.md) 语句定义），则该数据块将作为一个事务提交，以便您在必要时对其进行回滚。此行为的例外是 TRUNCATE 和 VACUUM 命令，这些命令可自动提交当前事务中所做的所有待定更改。  
某些 SQL 客户端会自动发出 BEGIN 和 COMMIT 命令，因此客户端控制着是一组语句作为一个事务运行，还是每个单独的语句作为自己的事务运行。检查您正在使用的界面的文档。例如，使用 Amazon Redshift JDBC 驱动程序时，具有包含多个（分号分隔）SQL 命令的查询字符串的 JDBC `PreparedStatement` 将所有语句作为单个事务运行。相比之下，如果您使用 SQL Workbench/J 并设置 AUTO COMMIT ON，则如果您运行多个语句，每个语句都会作为自己的事务运行。

以下主题介绍一些主要概念和使用案例，它们涉及事务、数据库快照、更新和并发行为。

**Topics**
+ [Amazon Redshift 中的隔离级别](c_serial_isolation.md)
+ [写入和读/写操作](c_write_readwrite.md)
+ [并发写入示例](r_Serializable_isolation_example.md)
+ [可序列化隔离错误排查](c_serial_isolation-serializable-isolation-troubleshooting.md)

# Amazon Redshift 中的隔离级别
<a name="c_serial_isolation"></a>

在 Amazon Redshift 中，以保护性方式支持并发写入操作，即对表使用写入锁定和*可序列化的隔离*原则。可序列化的隔离会保留一种错觉，即对某个表运行的事务是对该表运行的唯一事务。

Amazon Redshift 数据库采用在事务开始时让每个操作使用其数据的最新提交版本或快照的方法，以此来支持并发写入操作。在大多数 SELECT 语句、DML 命令（例如 COPY、DELETE、INSERT、UPDATE 和 TRUNCATE）和以下 DDL 命令首次出现时，将在事务中创建数据库快照：
+  ALTER TABLE（添加或删除列） 
+  CREATE TABLE 
+  DROP TABLE 
+  TRUNCATE TABLE 

任何其他事务都无法更改此快照，这意味着事务是相互隔离的。也就是说，并发事务彼此不可见，它们不能相互检测对方的更改。

任意并发执行事务得到的结果，都必须与顺序执行这些事务得到的结果相同。如果这些事务的任何序列执行均不产生相同结果，则执行会破坏可序列性的语句的事务将被中止并回滚。

例如，假设用户尝试运行两个并发事务 T1 和 T2。运行 T1 和 T2 必须生成与以下至少一个场景相同的结果：
+ T1 和 T2 依次运行。
+ T2 和 T1 依次运行。

 Amazon Redshift 中的隔离级别可以防止出现以下问题：
+  脏读：在事务读取尚未提交的数据时，就会发生脏读。例如，假设事务 1 更新了一行。事务 2 在 T1 提交更新之前读取更新的行。如果 T1 回滚更改，则 T2 将读取未提交行中的数据，而 Amazon Redshift 现在认为这些数据从不存在。
+  不可重复的读取：当某个事务两次读取同一行但每次均获得不同的数据时，就会发生不可重复的读取。例如，假设事务 1 读取了一行。事务 2 更新或删除该行并提交了更新或删除。如果 T1 再次读取该行，它会检索到不同的行值或发现该行已删除。
+  幻象：幻象是指某行符合搜索条件，但该行并非最初看到的行。例如，假设事务 1 读取满足其搜索条件的一组行。事务 2 在 UPDATE 或 INSERT 语句中生成了新行，该行与 T1 搜索条件匹配。如果 T1 重新运行其搜索语句，就会得到一组不同的行。

## SNAPSHOT 和 SERIALIZABLE 隔离
<a name="c_serial_isolation-snapshot_and_serializable"></a>

SERIALIZABLE 和 SNAPSHOT 隔离是 Amazon Redshift 中提供的两种可序列化隔离级别。

SNAPSHOT 隔离是创建预调配集群和无服务器工作组时的默认隔离级别，相比 SERIALIZABLE 隔离，此隔离让您可在更短的时间内处理更大量的数据。

SERIALIZABLE 隔离用时更长，但对并发事务实施了更严格的限制。此隔离级别仅允许提交一个事务，同时取消所有其他并发事务并给出可序列化隔离违规错误，从而防止写入偏斜异常等问题。

以下时间线示例说明使用 SNAPSHOT 隔离时，系统如何处理两个并发写入操作。系统允许提交每个用户的 UPDATE 语句，因为它们不会因为尝试更新相同的行而发生冲突。

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

如果使用可序列化的隔离运行同一场景，则 Amazon Redshift 因可序列化违规而终止用户 2 并返回错误 `1023`。有关更多信息，请参阅 [可序列化隔离错误排查](c_serial_isolation-serializable-isolation-troubleshooting.md)。在这种情况下，只有用户 1 可以成功提交。

## 注意事项
<a name="c_serial_isolation-considerations"></a>

在 Amazon Redshift 中使用隔离级别时，请考虑以下事项：
+  查询 STV\$1DB\$1ISOLATION\$1LEVEL 目录视图来查看数据库使用的隔离级别。有关更多信息，请参阅 [STV\$1DB\$1ISOLATION\$1LEVEL](r_STV_DB_ISOLATION_LEVEL.md)。
+  查询 PG\$1DATABASE\$1INFO 视图来查看您的数据库支持多少个并发事务。有关更多信息，请参阅 [PG\$1DATABASE\$1INFO](r_PG_DATABASE_INFO.md)。
+  系统目录表（PG）和其他 Amazon Redshift 系统表在事务中未锁定。因此，DDL 和 TRUNCATE 操作引起的数据库对象更改在提交到任何并发事务时均可见。

   例如，假设在两个并发事务（T1 和 T2）开始时，数据库中存在表 A。假设 T2 通过从 PG\$1TABLES 目录表中进行选择来返回表列表。然后 T1 删除表 A 并提交，T2 再次列出这些表。现在不再列出表 A。如果 T2 尝试查询已删除的表，则 Amazon Redshift 将返回“relation does not exist”错误。向 T2 返回表列表或检查表 A 是否存在的目录查询不受与针对用户表的操作相同的隔离规则的约束。

   更新这些表的事务在读取已提交 隔离模式下运行。
+  前缀为 PG 的目录表不支持 SNAPSHOT 隔离。

# 写入和读/写操作
<a name="c_write_readwrite"></a>

可通过决定何时以及如何运行不同类型的命令来管理并发写入操作的特定行为。以下命令与此讨论相关：
+ COPY 命令，可执行加载（初始或增量）
+ INSERT 命令，可一次性追加一个或多个行
+ UPDATE 命令，可修改现有行
+ DELETE 命令，可删除行 

COPY 和 INSERT 操作是纯写入操作。DELETE 和 UPDATE 操作是读/写操作（对于要删除或更新的行，必须先读取它们）。并发写入操作的结果取决于同时运行的具体命令。

UPDATE 和 DELETE 操作的行为方式不同，因为它们在执行任何写入操作前依赖最初的表读取。由于并发事务彼此不可见，因此，UPDATE 和 DELETE 必须读取上次提交的数据的快照。当第一个 UPDATE 或 DELETE 解除其锁定时，第二个 UPDATE 或 DELETE 需要确定它将使用的数据是否可能已过时。它不会过时，因为第二个事务不会在第一个事务解除其锁定之前获取其数据的快照。

## 涉及多个表的并发写入事务的潜在死锁情况
<a name="c_write_readwrite-potential-deadlock"></a>

当事务涉及更新多个表时，并发运行的事务始终可能在同时尝试写入同一组表时变为死锁状态。事务会在提交或回滚时一次性解除其所有表锁定；而不会逐一放弃锁定。

例如，假设事务 T1 和 T2 在大致相同的时间开始。如果 T1 开始对表 A 进行写入，而 T2 开始对表 B 进行写入，则这两个事务均可继续而不会发生冲突。但是，如果 T1 完成了对表 A 的写入操作并需要开始对表 B 进行写入，它将无法继续，因为 T2 仍保持对 B 的锁定。同样，如果 T2 完成了对表 B 的写入操作并需要开始对表 A 进行写入，它也无法继续，因为 T1 仍保持对 A 的锁定。由于两个事务都不能在提交其所有写入操作之前解除其锁定，因此两个事务都不能继续。为避免发生这种死锁情况，您需要小心安排并发写入操作。例如，您应始终在各事务中按相同顺序更新表，如果指定了锁定，则应先按相同的顺序锁定表，然后再执行任何 DML 操作。

## 涉及单个表的并发写入事务的潜在死锁情况
<a name="c_write_readwrite-potential-deadlock-single"></a>

在快照隔离环境中，对于同一个表运行并发写入事务时可能会发生死锁。当并发的 INSERT 或 COPY 语句共享锁定并取得进展，而另一条语句需要对同一个表执行需要独占锁定的操作（UPDATE、DELETE、MERGE 或 DDL 操作）时，就会发生快照隔离死锁。

考虑以下情况：

事务 1（T1）：

```
INSERT/COPY INTO table_A;
```

事务 2（T2）：

```
INSERT/COPY INTO table_A; 
            <UPDATE/DELETE/MERGE/DDL statement> table_A
```

当在具有共享锁定的同一个表上并发运行多个带有 INSERT 或 COPY 操作的事务时，其中一个事务在纯写入操作之后执行一个需要独占锁定的操作（例如 UPDATE、MERGE、DELETE 或 DDL 语句）时，可能会发生死锁。

为了避免在这些情况下发生死锁，可以将需要独占锁定的语句（UPDATE/MERGE/DELETE/DDL 语句）分开到不同的事务，这样，任何 INSERT/COPY 语句都可以同时进行，并且需要独占锁定的语句可以在它们之后执行。或者，对于在同一个表上具有 INSERT/COPY 语句和 MERGE/UPDATE/MERGE 语句的事务，您可以在应用程序中加入重试逻辑来解决潜在的死锁。

# 并发写入示例
<a name="r_Serializable_isolation_example"></a>

以下伪代码示例演示事务如何在并行运行时继续或等待。

## 使用可序列化隔离的并发写入示例
<a name="r_Serializable_isolation_example-serializable"></a>

### 使用可序列化隔离的到相同表的并发 COPY 操作
<a name="r_Serializable_isolation_example-concurrent-copy-operations-into-the-same-table"></a>

事务 1 将行复制到 LISTING 表中：

```
begin;
copy listing from ...;
end;
```

事务 2 在单独的会话中同时开始，并尝试将多个行复制到 LISTING 表中。事务 2 必须等待事务 1 解除对 LISTING 表的写入锁定，然后才能继续。

```
begin;
[waits]
copy listing from ;
end;
```

如果一个或两个事务包含 INSERT 命令而非 COPY 命令，也会产生相同行为。

### 使用可序列化隔离的来自相同表的并发 DELETE 操作
<a name="r_Serializable_isolation_example-concurrent-delete-operations-from-the-same-table"></a>

事务 1 从表中删除行：

```
begin;
delete from listing where ...;
end;
```

事务 2 同时开始并尝试从相同表中删除行。它将成功，因为它会等待事务 1 完成后再尝试删除行。

```
begin
[waits]
delete from listing where ;
end;
```

如果一个或两个事务包含对相同表的 UPDATE 命令而非 DELETE 命令，也会产生相同行为。

### 使用可序列化隔离的具有读取和写入操作组合的并发事务
<a name="r_Serializable_isolation_example-concurrent-transactions"></a>

在此示例中，在提交之前，事务 1 从 USERS 表中删除行、重新加载表、运行 COUNT(\$1) 查询，然后 ANALYZE：

```
begin;
delete one row from USERS table;
copy ;
select count(*) from users;
analyze ;
end;
```

同时，事务 2 将开始。此事务尝试将额外的行复制到 USERS 表中、分析该表，然后运行与第一个事务相同的 COUNT(\$1) 查询：

```
begin;
[waits]
copy users from ...;
select count(*) from users;
analyze;
end;
```

第二个事务将成功，因为它必须等待第一个事务完成。其 COUNT 查询将返回基于已完成的加载的计数。

## 使用快照隔离的并发写入示例
<a name="r_Serializable_isolation_example-snapshot"></a>

### 使用快照隔离的到相同表的并发 COPY 操作
<a name="r_Serializable_isolation_example-concurrent-copy-operations-into-the-same-table-snapshot"></a>

事务 1 将行复制到 LISTING 表中：

```
begin;
copy listing from ...;
end;
```

事务 2 在单独的会话中同时开始，并尝试将多个行复制到 LISTING 表中。事务 2 可以同时进行，直到任一事务都需要向目标表 `listing` 写入数据，此时它们将按顺序运行。

```
begin; 
//When the COPY statement from T1 needs to write data to the table, the COPY statement from T2 waits.
copy listing from ...; 
end;
```

如果一个或两个事务包含 INSERT 命令而非 COPY 命令，也会产生相同行为。

### 使用快照隔离的来自相同表的并发 DELETE 操作
<a name="r_Serializable_isolation_example-concurrent-delete-operations-from-the-same-table-snapshot"></a>

使用快照隔离的来自相同表的并发 DELETE 或 UPDATE 操作的运行方式与使用可序列化隔离运行的操作相同。

### 使用快照隔离的具有读取和写入操作组合的并发事务
<a name="r_Serializable_isolation_example-concurrent-transactions-snapshot"></a>

使用快照隔离的组合操作运行的并发事务的运行方式与使用可序列化隔离运行的组合操作的事务相同。

# 可序列化隔离错误排查
<a name="c_serial_isolation-serializable-isolation-troubleshooting"></a>

## ERROR:1023 DETAIL：Redshift 中的表上的可序列化隔离冲突
<a name="c_serial_isolation-serialization-isolation-1023"></a>

当 Amazon Redshift 检测到可序列化的隔离错误时，您会看到错误消息，如下所示。

```
ERROR:1023 DETAIL: Serializable isolation violation on table in Redshift
```

要解决可序列化的隔离错误，您可以尝试以下方法：
+ 重试已取消的事务。

   Amazon Redshift 检测到并发工作负载不可序列化。它建议使应用程序逻辑中存在差异，而这些差异通常可以通过重试遇到错误的事务来解决。如果问题仍然存在，请尝试使用其他方法之一。
+ 将任何不必在同一个原子事务中的操作移到事务之外。

  当两个事务内的各个操作以可能影响另一个事务的结果的方式相互交叉引用时，此方法适用。例如，以下两个会话各自启动一个事务。

  ```
  Session1_Redshift=# begin;
  ```

  ```
  Session2_Redshift=# begin;
  ```

  每个事务中的 SELECT 语句的结果可能受另一个事务中的 INSERT 语句影响。换句话说，假设您以任何顺序连续运行以下语句。在每种情况下，如果不是同时运行事务，结果是 SELECT 语句之一另外返回一行。操作顺序运行时，没有哪种顺序可以产生与并发运行时相同的结果。因此，运行的最后一个操作会导致可序列化的隔离错误。

  ```
  Session1_Redshift=# select * from tab1;
  Session1_Redshift=# insert into tab2 values (1);
  ```

  ```
  Session2_Redshift=# insert into tab1 values (1);
  Session2_Redshift=# select * from tab2;
  ```

  在许多情况下，SELECT 语句的结果并不重要。换句话说，事务中操作的原子性并不重要。在这些情况下，将 SELECT 语句移到事务之外，如以下示例所示。

  ```
  Session1_Redshift=# begin;
  Session1_Redshift=# insert into tab1 values (1)
  Session1_Redshift=# end;
  Session1_Redshift=# select * from tab2;
  ```

  ```
  Session2_Redshift # select * from tab1;
  Session2_Redshift=# begin;
  Session2_Redshift=# insert into tab2 values (1)
  Session2_Redshift=# end;
  ```

  在这些示例中，事务中没有交叉引用。两个 INSERT 语句不会相互影响。在这些示例中，至少有一个顺序，其中事务可以顺序运行并产生与并发运行时相同的结果。这意味着事务是可序列化的。
+ 通过锁定每个会话中的所有表来强制序列化。

  [LOCK](r_LOCK.md) 命令阻止可能导致可序列化隔离错误的操作。使用 LOCK 命令时，请确保执行以下操作：
  + 锁定受事务影响的所有表，包括受事务内部的只读 SELECT 语句影响的表。
  + 无论执行操作的顺序如何，都以相同的顺序锁定表。
  + 在执行任何操作之前，在事务开始时锁定所有表。
+ 对并发事务使用快照隔离

  将 ALTER DATABASE 命令与快照隔离功能结合使用。有关 ALTER DATABASE 的 SNAPSHOT 参数的更多信息，请参阅[参数](r_ALTER_DATABASE.md#r_ALTER_DATABASE-parameters)。

## ERROR:1018 DETAIL：关系不存在
<a name="c_serial_isolation-serialization-isolation-1018"></a>

当您在不同会话中运行 Amazon Redshift 并发操作时，您会看到错误消息，如下所示。

```
ERROR: 1018 DETAIL: Relation does not exist.
```

Amazon Redshift 中的事务遵循快照隔离。在事务开始后，Amazon Redshift 将拍摄数据库的快照。对于事务的整个生命周期，事务在快照中反映的数据库状态下运行。如果事务从快照中不存在的表读取，则会引发之前显示的 1018 错误消息。即使另一个并发事务在事务拍摄快照后创建表，该事务也无法从新创建的表中读取。

要解决此序列化隔离错误，您可以尝试将事务的起始位置移动到您所知道的该表存在的位置。

如果表是由另一个事务创建的，则此位置至少在该事务提交之后。此外，请确保没有提交可能已删除表的并发事务。

```
session1 = # BEGIN;
session1 = # DROP TABLE A;
session1 = # COMMIT;
```

```
session2 = # BEGIN;
```

```
session3 = # BEGIN;
session3 = # CREATE TABLE A (id INT);
session3 = # COMMIT;
```

```
session2 = # SELECT * FROM A;
```

作为 session2 的读取操作运行的最后一个操作会导致可序列化的隔离错误。当 session2 拍摄快照并且表已被提交的 session1 删除时，会发生此错误。换句话说，即使并发 session3 已创建表，session2 也不会看到该表，因为它不在快照中。

要解决此错误，您可以按以下方式对会话重新排序。

```
session1 = # BEGIN;
session1 = # DROP TABLE A;
session1 = # COMMIT;
```

```
session3 = # BEGIN;
session3 = # CREATE TABLE A (id INT);
session3 = # COMMIT;
```

```
session2 = # BEGIN;
session2 = # SELECT * FROM A;
```

现在，当 session2 拍摄快照时，session3 已经提交，并且表位于数据库中。Session2 可以从表中读取，而不会出现任何错误。

# 教程：从 Amazon S3 加载数据
<a name="tutorial-loading-data"></a>

在本教程中，您将了解从 Amazon S3 桶中的数据文件将数据加载到您的 Amazon Redshift 数据库表中的完整过程。

在本教程中，您将执行以下操作：
+ 下载使用逗号分隔值 (CSV) 格式、字符分隔格式和固定宽度格式的数据文件。
+ 创建一个 Amazon S3 桶，然后将数据文件上载到该桶。
+ 启动 Amazon Redshift 集群并创建数据库表。
+ 使用 COPY 命令从 Amazon S3 上的数据文件加载表。
+ 诊断加载错误并修改您的 COPY 命令来更正这些错误。

## 先决条件
<a name="tutorial-loading-data-prerequisites"></a>

您需要以下先决条件：
+ 用于启动 Amazon Redshift 集群并在 Amazon S3 中创建桶的 AWS 账户。
+ 您从 Amazon S3 加载测试数据的 AWS 凭证（IAM 角色）。如果您需要一个新的 IAM 角色，请转到[创建 IAM 角色](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create.html)。
+ SQL 客户端，如 Amazon Redshift 控制台查询编辑器。

本教程设计为单独使用。除了本教程之外，还建议您完成以下教程来更全面地了解如何设计和使用 Amazon Redshift 数据库：
+ [Amazon Redshift 入门指南](https://docs.aws.amazon.com/redshift/latest/gsg/)将指导您完成创建 Amazon Redshift 集群和加载示例数据的过程。

## 概述
<a name="tutorial-loading-data-overview"></a>

您可以通过使用 INSERT 命令或 COPY 命令来将数据添加到您的 Amazon Redshift 表。在 Amazon Redshift 数据仓库的规模和速度方面，COPY 命令要比 INSERT 命令快许多倍且更高效。

COPY 命令使用 Amazon Redshift 大规模并行处理 (MPP) 架构来从多个数据来源并行读取和加载数据。您可以从 Amazon S3、Amazon EMR 或任何可通过 Secure Shell (SSH) 连接访问的远程主机上的数据文件加载。或者，您可以直接从 Amazon DynamoDB 表加载。

在本教程中，您将使用 COPY 命令来从 Amazon S3 加载数据。此处提到的许多原则也适用于从其他数据来源加载。

要了解有关使用 COPY 命令的更多信息，请参阅以下资源：
+ [Amazon Redshift 加载数据的最佳实践](c_loading-data-best-practices.md)
+ [从 Amazon EMR 中加载数据](loading-data-from-emr.md)
+ [从远程主机中加载数据](loading-data-from-remote-hosts.md)
+ [从 Amazon DynamoDB 表中加载数据](t_Loading-data-from-dynamodb.md)

## 步骤 1：创建集群
<a name="tutorial-loading-data-launch-cluster"></a>

如果您已有要使用的集群，则可跳过这一步。

在本教程的练习中，将使用四节点集群。

**创建集群**

1. 登录到 AWS 管理控制台并打开 Amazon Redshift 控制台，网址：[https://console.aws.amazon.com/redshiftv2/](https://console.aws.amazon.com/redshiftv2/)。

   使用导航菜单，选择**预置集群控制面板**
**重要**  
请确保您具有执行集群操作所需的权限。有关授予必要权限的信息，请参阅[授权 Amazon Redshift 访问 AWS 服务](https://docs.aws.amazon.com/redshift/latest/mgmt/authorizing-redshift-service.html)。

1. 在右上角，选择要在其中创建集群的 AWS 区域。在本教程中，选择**美国西部（俄勒冈州）**。

1. 在导航菜单上，选择**集群**，然后选择**创建集群**。此时将显示**创建集群**页面。

1. 在**创建集群**页面输入您的集群参数。除更改以下值外，选择您自己的参数值：
   + 选择节点类型 **dc2.large**。
   + 为**节点数量**选择 **4**。
   + 在**集群权限**部分中，从**可用 IAM 角色**中选择一个 IAM 角色。此角色应是您之前创建的有权访问 Amazon S3 的角色。然后，选择**关联 IAM 角色**以将该角色添加到集群的**关联的 IAM 角色**列表中。

1. 选择**创建集群**。

按 [Amazon Redshift 入门指南](https://docs.aws.amazon.com/redshift/latest/gsg/)中的步骤操作，以从 SQL 客户端连接到您的集群并测试连接。您无需完成此入门中的剩余步骤，即可创建表、上传数据和尝试示例查询。

## 步骤 2：下载数据文件
<a name="tutorial-loading-data-download-files"></a>

在此步骤中，您将一组示例数据文件下载到计算机。在下一个步骤中，您将这些文件上载到 Amazon S3 桶。

**下载数据文件**

1. 下载压缩文件：[LoadingDataSampleFiles.zip](samples/LoadingDataSampleFiles.zip)。

1. 将文件提取到您计算机上的文件夹中。

1. 验证您的文件夹是否包含以下文件。

   ```
   customer-fw-manifest
   customer-fw.tbl-000
   customer-fw.tbl-000.bak
   customer-fw.tbl-001
   customer-fw.tbl-002
   customer-fw.tbl-003
   customer-fw.tbl-004
   customer-fw.tbl-005
   customer-fw.tbl-006
   customer-fw.tbl-007
   customer-fw.tbl.log
   dwdate-tab.tbl-000
   dwdate-tab.tbl-001
   dwdate-tab.tbl-002
   dwdate-tab.tbl-003
   dwdate-tab.tbl-004
   dwdate-tab.tbl-005
   dwdate-tab.tbl-006
   dwdate-tab.tbl-007
   part-csv.tbl-000
   part-csv.tbl-001
   part-csv.tbl-002
   part-csv.tbl-003
   part-csv.tbl-004
   part-csv.tbl-005
   part-csv.tbl-006
   part-csv.tbl-007
   ```

## 步骤 3：将文件上载到 Amazon S3 桶
<a name="tutorial-loading-data-upload-files"></a>

在此步骤中，您将创建一个 Amazon S3 桶并将数据文件上载到该桶。

### 
<a name="tutorial-loading-data-to-upload-files"></a>

**要将文件上载到 Amazon S3 桶**

1. 在 Amazon S3 中创建一个桶。

   有关创建存储桶的更多信息，请参阅《Amazon Simple Storage Service 用户指南》**中的[创建存储桶](https://docs.aws.amazon.com/AmazonS3/latest/userguide/create-bucket-overview.html)。

   1. 登录到 AWS 管理控制台，然后通过以下网址打开 Amazon S3 控制台：[https://console.aws.amazon.com/s3/](https://console.aws.amazon.com/s3/)。

   1. 选择**创建桶**。

   1. Choose an AWS 区域。

      在集群所在的区域中创建桶。如果您的集群位于美国西部（俄勒冈州）区域，请选择**美国西部（俄勒冈州）区域 (us-west-2)**。

   1. 在**创建桶**对话框的**桶名称**框中，输入桶名称。

      所选的桶名称在 Amazon S3 的所有现有桶名称中必须具有唯一性。确保唯一性的一种办法是以您所在的组织的名称作为您的桶名称的前缀。桶名称必须符合特定规则。有关更多信息，请转至《Amazon Simple Storage Service 用户指南》**中的[桶限制](https://docs.aws.amazon.com/AmazonS3/latest/userguide/BucketRestrictions.html)。

   1. 为其余选项选择推荐的默认值。

   1. 选择**创建桶**。

      Amazon S3 成功创建桶后，控制台的**桶**面板中将显示空桶。

1. 创建一个文件夹。

   1. 选择新桶的名称。

   1. 选择**创建文件夹**按钮。

   1. 将新文件夹命名为 **load**。
**注意**  
您创建的桶不会显示在沙盒中。在本练习中，您将对象添加到实际桶。您需要根据对象在桶中存储的时间支付象征性的费用。有关 Amazon S3 定价的更多信息，请前往 [Amazon S3 定价](https://aws.amazon.com/s3/pricing/)页面。

1. 将数据文件上载到新的 Amazon S3 桶。

   1. 选择数据文件夹的名称。

   1. 在上传向导中，选择**添加文件**。

      按照 Amazon S3 控制台的说明上载您下载并提取的所有文件。

   1. 选择**上传**。
<a name="tutorial-loading-user-credentials"></a>
**用户凭证**  
Amazon Redshift COPY 命令必须具有对 Amazon S3 桶中的文件对象的读访问权。如果您使用同一用户凭证来创建 Amazon S3 桶并运行 Amazon Redshift COPY 命令，则 COPY 命令将具有所有必要权限。如果您希望使用其他用户凭证，则可以通过使用 Amazon S3 访问控制来授予访问权限。Amazon Redshift COPY 命令至少需要 ListBucket 和 GetObject 权限才能访问 Amazon S3 桶中的文件对象。有关如何控制对 Amazon S3 资源的访问权限的更多信息，请前往[管理您的 Amazon S3 资源的访问权限](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-access-control.html)。

## 步骤 4：创建示例表
<a name="tutorial-loading-data-create-tables"></a>

对于本教程，您将基于 Star Schema Benchmark（SSB）架构使用一组表。下图显示了 SSB 数据模型。

![\[SSB 架构的五个表及其相互关系。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/tutorial-optimize-tables-ssb-data-model.png)


SSB 表可能已存在于当前数据库中。如果是这样的话，请先从数据库中删除这些表，然后再在下一步骤中使用 CREATE TABLE 命令创建这些表。本教程中使用的表可能包含与现有表不同的属性。

**创建示例表**

1. 要删除 SSB 表，请在 SQL 客户端中运行以下命令。

   ```
   drop table part cascade;
   drop table supplier;
   drop table customer;
   drop table dwdate;
   drop table lineorder;
   ```

1. 在 SQL 客户端中运行以下 CREATE TABLE 命令。

   ```
   CREATE TABLE part 
   (
     p_partkey     INTEGER NOT NULL,
     p_name        VARCHAR(22) NOT NULL,
     p_mfgr        VARCHAR(6),
     p_category    VARCHAR(7) NOT NULL,
     p_brand1      VARCHAR(9) NOT NULL,
     p_color       VARCHAR(11) NOT NULL,
     p_type        VARCHAR(25) NOT NULL,
     p_size        INTEGER NOT NULL,
     p_container   VARCHAR(10) NOT NULL
   );
   
   CREATE TABLE supplier 
   (
     s_suppkey   INTEGER NOT NULL,
     s_name      VARCHAR(25) NOT NULL,
     s_address   VARCHAR(25) NOT NULL,
     s_city      VARCHAR(10) NOT NULL,
     s_nation    VARCHAR(15) NOT NULL,
     s_region    VARCHAR(12) NOT NULL,
     s_phone     VARCHAR(15) NOT NULL
   );
   
   CREATE TABLE customer 
   (
     c_custkey      INTEGER NOT NULL,
     c_name         VARCHAR(25) NOT NULL,
     c_address      VARCHAR(25) NOT NULL,
     c_city         VARCHAR(10) NOT NULL,
     c_nation       VARCHAR(15) NOT NULL,
     c_region       VARCHAR(12) NOT NULL,
     c_phone        VARCHAR(15) NOT NULL,
     c_mktsegment   VARCHAR(10) NOT NULL
   );
   
   CREATE TABLE dwdate 
   (
     d_datekey            INTEGER NOT NULL,
     d_date               VARCHAR(19) NOT NULL,
     d_dayofweek          VARCHAR(10) NOT NULL,
     d_month              VARCHAR(10) NOT NULL,
     d_year               INTEGER NOT NULL,
     d_yearmonthnum       INTEGER NOT NULL,
     d_yearmonth          VARCHAR(8) NOT NULL,
     d_daynuminweek       INTEGER NOT NULL,
     d_daynuminmonth      INTEGER NOT NULL,
     d_daynuminyear       INTEGER NOT NULL,
     d_monthnuminyear     INTEGER NOT NULL,
     d_weeknuminyear      INTEGER NOT NULL,
     d_sellingseason      VARCHAR(13) NOT NULL,
     d_lastdayinweekfl    VARCHAR(1) NOT NULL,
     d_lastdayinmonthfl   VARCHAR(1) NOT NULL,
     d_holidayfl          VARCHAR(1) NOT NULL,
     d_weekdayfl          VARCHAR(1) NOT NULL
   );
   CREATE TABLE lineorder 
   (
     lo_orderkey          INTEGER NOT NULL,
     lo_linenumber        INTEGER NOT NULL,
     lo_custkey           INTEGER NOT NULL,
     lo_partkey           INTEGER NOT NULL,
     lo_suppkey           INTEGER NOT NULL,
     lo_orderdate         INTEGER NOT NULL,
     lo_orderpriority     VARCHAR(15) NOT NULL,
     lo_shippriority      VARCHAR(1) NOT NULL,
     lo_quantity          INTEGER NOT NULL,
     lo_extendedprice     INTEGER NOT NULL,
     lo_ordertotalprice   INTEGER NOT NULL,
     lo_discount          INTEGER NOT NULL,
     lo_revenue           INTEGER NOT NULL,
     lo_supplycost        INTEGER NOT NULL,
     lo_tax               INTEGER NOT NULL,
     lo_commitdate        INTEGER NOT NULL,
     lo_shipmode          VARCHAR(10) NOT NULL
   );
   ```

## 步骤 5：运行 COPY 命令
<a name="tutorial-loading-run-copy"></a>

您将运行 COPY 命令来加载 SSB Schema 中的每个表。该 COPY 命令示例演示了使用多个 COPY 命令选项从不同文件格式加载并诊断加载错误。

### COPY 命令语法
<a name="tutorial-loading-data-copy-syntax"></a>

基本 [COPY](r_COPY.md) 命令语法如下所示。

```
COPY table_name [ column_list ] FROM data_source CREDENTIALS access_credentials [options] 
```

要运行 COPY 命令，您需提供以下值。
<a name="tutorial-loading-syntax-table-name"></a>
**表名称**  
COPY 命令的目标表。该表必须已存在于数据库中。该表可以是临时的或永久的。COPY 命令会将新输入数据追加到该表中的任何现有行。
<a name="tutorial-loading-syntax-column-list"></a>
**列列表**  
默认情况下，COPY 按顺序将源数据中的字段加载到表列中。您可以选择指定一个*列列表*（该列表是列名称的以逗号分隔的列表）以将数据字段映射到特定列。您在本教程中不使用列列表。有关更多信息，请参阅 COPY 命令参考中的[Column List](copy-parameters-column-mapping.md#copy-column-list)。

<a name="tutorial-loading-syntax-data-source.title"></a>数据来源

您可以使用 COPY 命令从 Amazon S3 桶、Amazon EMR 集群、使用 SSH 连接的远程主机或 Amazon DynamoDB 表加载数据。在本教程中，您将从 Amazon S3 桶中的数据文件进行加载。在从 Amazon S3 进行加载时，您必须提供桶的名称和数据文件的位置。为此，请提供数据文件的对象路径或清单文件的位置，该清单文件明确列出了各个数据文件及其位置。
+ 键前缀 

  存储在 Amazon S3 中的对象由对象键唯一标识，其中包括桶名称、文件夹名称（如果有）和对象名称。*键前缀* 是指一组带有相同前缀的对象。对象路径是 COPY 命令用于加载共享键前缀的所有对象的键前缀。例如，键前缀 `custdata.txt` 可以是一个文件或一组文件，包括 `custdata.txt.001`、`custdata.txt.002` 等。
+ 清单文件

  在某些情况下，您可能需要加载具有不同前缀的文件，例如，从多个桶或文件夹中进行加载。在其他情况下，您可能需要排除共享前缀的文件。在这些情况下，您可以使用清单文件。*清单文件* 明确列出了每个加载文件及其唯一对象键。在本教程的后面，您将使用清单文件来加载 PART 表。
<a name="tutorial-loading-syntax-credentials"></a>
**凭证**  
要访问包含要加载的数据的 AWS 资源，您必须为具有足够权限的用户提供 AWS 访问凭证。这些凭证包括 IAM 角色的 Amazon 资源名称（ARN）。要从 Amazon S3 加载数据，凭证必须包括 ListBucket 和 GetObject 权限。如果数据已加密，则需要其它凭证。有关更多信息，请参阅 COPY 命令参考中的[授权参数](copy-parameters-authorization.md)。有关管理访问的更多信息，请转到[管理对 Amazon S3 资源的访问权限](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-access-control.html)。

<a name="tutorial-loading-syntax-options.title"></a>选项

您可以为 COPY 命令指定大量参数，以指定文件格式、管理数据格式、管理错误和控制其他功能。在本教程中，您将使用以下 COPY 命令选项和功能：
+ 键前缀

  有关如何通过指定键前缀从多个文件加载的信息，请参阅[使用 NULL AS 加载 PART 表](#tutorial-loading-load-part)。
+ CSV 格式

  有关如何加载 CSV 格式的数据的信息，请参阅[使用 NULL AS 加载 PART 表](#tutorial-loading-load-part)。
+ NULL AS

  有关如何使用 NULL AS 选项加载 PART 的信息，请参阅[使用 NULL AS 加载 PART 表](#tutorial-loading-load-part)。
+ 字符分隔的格式

  有关如何使用 DELIMITER 选项的信息，请参阅[DELIMITER 和 REGION 选项](#tutorial-loading-load-supplier)。
+ REGION

  有关如何使用 REGION 选项的信息，请参阅[DELIMITER 和 REGION 选项](#tutorial-loading-load-supplier)。
+ 固定格式宽度

  有关如何从固定宽度数据加载 CUSTOMER 表的信息，请参阅[使用 MANIFEST 加载 CUSTOMER 表](#tutorial-loading-load-customer)。
+ MAXERROR

  有关如何使用 MAXERROR 选项的信息，请参阅[使用 MANIFEST 加载 CUSTOMER 表](#tutorial-loading-load-customer)。
+ ACCEPTINVCHARS

  有关如何使用 ACCEPTINVCHARS 选项的信息，请参阅[使用 MANIFEST 加载 CUSTOMER 表](#tutorial-loading-load-customer)。
+ MANIFEST

  有关如何使用 MANIFEST 选项的信息，请参阅[使用 MANIFEST 加载 CUSTOMER 表](#tutorial-loading-load-customer)。
+ DATEFORMAT

  有关如何使用 DATEFORMAT 选项的信息，请参阅[使用 DATEFORMAT 加载 DWDATE 表](#tutorial-loading-load-dwdate)。
+ GZIP、LZOP 和 BZIP2

  有关如何压缩文件的信息，请参阅[加载多个数据文件](#tutorial-loading-load-lineorder)。
+ COMPUPDATE

  有关如何使用 COMPUPDATE 选项的信息，请参阅[加载多个数据文件](#tutorial-loading-load-lineorder)。
+ 多个文件

  有关如何加载多个文件的信息，请参阅[加载多个数据文件](#tutorial-loading-load-lineorder)。

### 加载 SSB 表
<a name="tutorial-loading-run-copy-load-tables"></a>

您将使用以下 COPY 命令加载 SSB Schema 中的每个表。针对每个表的命令演示了不同的 COPY 选项和疑难解答方法。

要加载 SSB 表，请按以下步骤操作：

1. [替换桶名称和 AWS 凭证](#tutorial-loading-run-copy-replaceables)

1. [使用 NULL AS 加载 PART 表](#tutorial-loading-load-part)

1. [使用 MANIFEST 加载 CUSTOMER 表](#tutorial-loading-load-customer)

1. [使用 DATEFORMAT 加载 DWDATE 表](#tutorial-loading-load-dwdate)

#### 替换桶名称和 AWS 凭证
<a name="tutorial-loading-run-copy-replaceables"></a>

本教程中的 COPY 命令采用以下格式显示。

```
copy table from 's3://<your-bucket-name>/load/key_prefix' 
credentials 'aws_iam_role=arn:aws:iam::<aws-account-id>:role/<role-name>'
options;
```

对于每个 COPY 命令，请执行以下操作：

1. 将 *<your-bucket-name>* 替换为您的集群所在区域中的桶名称。

   此步骤假定桶和集群位于同一区域中。或者，您也可以通过对 COPY 命令使用 [REGION](copy-parameters-data-source-s3.md#copy-region) 选项来指定区域。

1. 用您自己的 AWS 账户 和 IAM 角色替换 *<aws-account-id>* 和 *<role-name>*。用单引号引起来的凭证字符串的区段不得包含任何空格或换行符。请注意，ARN 格式可能与示例略有不同。运行 COPY 命令时，最好从 IAM 控制台复制角色的 ARN，以确保其准确无误。

#### 使用 NULL AS 加载 PART 表
<a name="tutorial-loading-load-part"></a>

在此步骤中，您将使用 CSV 和 NULL AS 选项加载 PART 表。

COPY 命令可从多个文件并行加载数据，这比从一个文件加载数据快得多。为了演示此原则，本教程中每个表的数据将拆分为 8 个文件，即使这些文件非常小。在后面的步骤中，您将比较从一个文件加载所需的时间与从多个文件加载所需的时间的差异。有关更多信息，请参阅 [加载数据文件](c_best-practices-use-multiple-files.md)。
<a name="tutorial-loading-key-prefix"></a>
**键前缀**  
您可以通过为文件集指定键前缀，或通过在清单文件中明确列出文件，来从多个文件加载。在此步骤中，您将使用键前缀。在后面的步骤中，您将使用清单文件。键前缀 `'s3://amzn-s3-demo-bucket/load/part-csv.tbl'` 加载 `load` 文件夹中的以下一组文件。

```
part-csv.tbl-000
part-csv.tbl-001
part-csv.tbl-002
part-csv.tbl-003
part-csv.tbl-004
part-csv.tbl-005
part-csv.tbl-006
part-csv.tbl-007
```
<a name="tutorial-loading-csv-format"></a>
**CSV 格式**  
CSV（表示用逗号分隔的值）是一种用于导入和导出电子表格数据的常见格式。CSV 比逗号分隔的格式更灵活，因为它使您能够在字段中包含带引号的字符串。CSV 格式的 COPY 的默认引号字符是双引号 (")，但您可以通过使用 QUOTE AS 选项指定另一种引号字符。在字段中使用引号字符时，应使用另一种引号字符对该字符进行转义。

PART 表中采用 CSV 格式的数据文件中的以下摘录显示了使用双引号引起来的字符串 (`"LARGE ANODIZED BRASS"`)。它还显示一个用引号引起来的字符串中使用两个双引号引起来的字符串 (`"MEDIUM ""BURNISHED"" TIN"`)。

```
15,dark sky,MFGR#3,MFGR#47,MFGR#3438,indigo,"LARGE ANODIZED BRASS",45,LG CASE
22,floral beige,MFGR#4,MFGR#44,MFGR#4421,medium,"PROMO, POLISHED BRASS",19,LG DRUM
23,bisque slate,MFGR#4,MFGR#41,MFGR#4137,firebrick,"MEDIUM ""BURNISHED"" TIN",42,JUMBO JAR
```

PART 表的数据包含将导致 COPY 命令失败的字符。在本练习中，您将诊断并纠正错误。

要加载采用 CSV 格式的数据，请将 `csv` 添加到您的 COPY 命令。运行以下命令可加载 PART 表。

```
copy part from 's3://<your-bucket-name>/load/part-csv.tbl' 
credentials 'aws_iam_role=arn:aws:iam::<aws-account-id>:role/<role-name>'
csv;
```

您可能会收到类似于以下内容的错误消息。

```
An error occurred when executing the SQL command:
copy part from 's3://amzn-s3-demo-bucket/load/part-csv.tbl' 
credentials' ...

ERROR: Load into table 'part' failed.  Check 'stl_load_errors' system table for details. [SQL State=XX000] 

Execution time: 1.46s

1 statement(s) failed.
1 statement(s) failed.
```

要获取有关错误的更多信息，请查询 STL\$1LOAD\$1ERRORS 表。以下查询使用 SUBSTRING 函数缩短列以方便阅读，并使用 LIMIT 10 减少返回的行数。您可以调整 `substring(filename,22,25)` 中的值以允许您的桶名称的长度。

```
select query, substring(filename,22,25) as filename,line_number as line, 
substring(colname,0,12) as column, type, position as pos, substring(raw_line,0,30) as line_text,
substring(raw_field_value,0,15) as field_text, 
substring(err_reason,0,45) as reason
from stl_load_errors 
order by query desc
limit 10;
```

```
 query  |    filename      | line |  column   |    type    | pos |      
--------+-------------------------+-----------+------------+------------+-----+----
 333765 | part-csv.tbl-000 |    1 |           |            |   0 |

 line_text        | field_text |                    reason
------------------+------------+----------------------------------------------
 15,NUL next,     |            | Missing newline: Unexpected character 0x2c f
```
<a name="tutorial-loading-null-as"></a>
**NULL AS**  
`part-csv.tbl` 数据文件使用 NUL 终止符（`\x000` 或 `\x0`）来指示 NULL 值。

**注意**  
尽管 NUL 和 NULL 的拼写非常相似，但两者并不相同。NUL 是一种带代码点 `x000` 的 UTF-8 字符，通常用于指示记录结束 (EOR)。NULL 是一个表示数据缺失的 SQL 值。

默认情况下，COPY 将 NUL 终止符视为 EOR 字符并终止记录，这通常会导致意外结果或错误。没有在文本数据中指示 NULL 的单个标准方法。因此，使用 NULL AS COPY 命令选项可以指定加载表时替换为 NULL 的字符。在本示例中，您希望 COPY 将 NUL 终止符视为 NULL 值。

**注意**  
接收 NULL 值的表列必须配置为*不可为 null*。即，该表列不得包含 CREATE TABLE 规格中的 NOT NULL 约束。

要使用 NULL AS 选项加载 PART，请运行以下 COPY 命令。

```
copy part from 's3://<your-bucket-name>/load/part-csv.tbl' 
credentials 'aws_iam_role=arn:aws:iam::<aws-account-id>:role/<role-name>' 
csv
null as '\000';
```

要验证 COPY 加载的 NULL 值，请运行以下命令以仅选择包含 NULL 的行。

```
select p_partkey, p_name, p_mfgr, p_category from part where p_mfgr is null;
```

```
 p_partkey |  p_name  | p_mfgr | p_category
-----------+----------+--------+------------
        15 | NUL next |        | MFGR#47
        81 | NUL next |        | MFGR#23
       133 | NUL next |        | MFGR#44 
(2 rows)
```

#### DELIMITER 和 REGION 选项
<a name="tutorial-loading-load-supplier"></a>

DELIMITER 和 REGION 选项对于了解如何加载数据很重要。
<a name="tutorial-loading-character-delimited-format"></a>
**字符分隔的格式**  
字符分隔的文件中的字段由某个特定字符（如竖线字符 (\$1)、逗号 (,) 或制表符 (\$1t)）分隔。字符分隔文件的可使用任一 ASCII 字符（包括非打印 ASCII 字符之一）作为分隔符。通过使用 DELIMITER 选项指定分隔符。默认分隔符是竖线字符 (\$1)。

SUPPLIER 表中数据的以下摘要使用竖线分隔的格式。

```
1|1|257368|465569|41365|19950218|2-HIGH|0|17|2608718|9783671|4|2504369|92072|2|19950331|TRUCK
1|2|257368|201928|8146|19950218|2-HIGH|0|36|6587676|9783671|9|5994785|109794|6|19950416|MAIL
```
<a name="tutorial-loading-region"></a>
**REGION**  
只要可能，您应在 Amazon Redshift 集群所在的 AWS 区域中找到加载数据。如果您的数据和集群位于同一区域中，则将减少延迟并避免跨区域数据传输成本。有关更多信息，请参阅 [Amazon Redshift 加载数据的最佳实践](c_loading-data-best-practices.md)。

如果您必须从另一个 AWS 区域加载数据，可使用 REGION 选项指定从中查找加载数据的 AWS 区域。如果您指定一个区域，则所有加载数据（包括清单文件）必须位于已命名的区域中。有关更多信息，请参阅 [REGION](copy-parameters-data-source-s3.md#copy-region)。

例如，如果您的集群位于美国东部（弗吉尼亚州北部）区域，而您的 Amazon S3 存储桶位于美国西部（俄勒冈州）区域，则以下 COPY 命令说明了如何从用竖线分隔的数据中加载 SUPPLIER 表。

```
copy supplier from 's3://amzn-s3-demo-bucket/ssb/supplier.tbl' 
credentials 'aws_iam_role=arn:aws:iam::<aws-account-id>:role/<role-name>' 
delimiter '|' 
gzip
region 'us-west-2';
```

#### 使用 MANIFEST 加载 CUSTOMER 表
<a name="tutorial-loading-load-customer"></a>

在此步骤中，您将使用 FIXEDWIDTH、MAXERROR、ACCEPTINVCHARS 和 MANIFEST 选项来加载 CUSTOMER 表。

本练习中的示例数据包含的字符将在 COPY 命令尝试加载数据时导致错误。您将使用 MAXERRORS 选项和 STL\$1LOAD\$1ERRORS 系统表来诊断加载错误，然后使用 ACCEPTINVCHARS 和 MANIFEST 选项来消除错误。
<a name="tutorial-loading-fixed-width"></a>
**固定宽度格式**  
固定宽度格式将每个字段定义为固定数量的字符，而不是使用分隔符隔开的字段。CUSTOMER 表中数据的以下摘要使用固定宽度格式。

```
1   Customer#000000001   IVhzIApeRb           MOROCCO  0MOROCCO  AFRICA      25-705 
2   Customer#000000002   XSTf4,NCwDVaWNe6tE   JORDAN   6JORDAN   MIDDLE EAST 23-453
3   Customer#000000003   MG9kdTD              ARGENTINA5ARGENTINAAMERICA     11-783
```

标签/宽度对的顺序必须与表列的顺序完全一致。有关更多信息，请参阅 [FIXEDWIDTH](copy-parameters-data-format.md#copy-fixedwidth)。

CUSTOMER 表数据的固定宽度规范字符串如下所示。

```
fixedwidth 'c_custkey:10, c_name:25, c_address:25, c_city:10, c_nation:15, 
c_region :12, c_phone:15,c_mktsegment:10'
```

要从固定宽度数据加载 CUSTOMER 表，请运行以下命令。

```
copy customer
from 's3://<your-bucket-name>/load/customer-fw.tbl'
credentials 'aws_iam_role=arn:aws:iam::<aws-account-id>:role/<role-name>' 
fixedwidth 'c_custkey:10, c_name:25, c_address:25, c_city:10, c_nation:15, c_region :12, c_phone:15,c_mktsegment:10';
```

您应收到类似于以下内容的错误消息。

```
An error occurred when executing the SQL command:
copy customer
from 's3://amzn-s3-demo-bucket/load/customer-fw.tbl'
credentials'...

ERROR: Load into table 'customer' failed.  Check 'stl_load_errors' system table for details. [SQL State=XX000] 

Execution time: 2.95s

1 statement(s) failed.
```
<a name="tutorial-loading-maxerror"></a>
**MAXERROR**  
原定设置情况下，COPY 首次遇到错误时，该命令将失败并返回错误消息。要在测试期间节省时间，您可以使用 MAXERROR 选项来指示 COPY 在跳过指定数量的错误后失败。由于我们预计了首次测试加载 CUSTOMER 表数据时的错误，因此将 `maxerror 10` 添加到 COPY 命令。

要使用 FIXEDWIDTH 和 MAXERROR 选项进行测试，请运行以下命令。

```
copy customer
from 's3://<your-bucket-name>/load/customer-fw.tbl'
credentials 'aws_iam_role=arn:aws:iam::<aws-account-id>:role/<role-name>' 
fixedwidth 'c_custkey:10, c_name:25, c_address:25, c_city:10, c_nation:15, c_region :12, c_phone:15,c_mktsegment:10'
maxerror 10;
```

这一次，您将收到类似于以下内容的警告消息，而不是错误消息。

```
Warnings:
Load into table 'customer' completed, 112497 record(s) loaded successfully.
Load into table 'customer' completed, 7 record(s) could not be loaded.  Check 'stl_load_errors' system table for details.
```

该警告指示 COPY 遇到了 7 个错误。要查看这些错误，请查询 STL\$1LOAD\$1ERRORS 表，如以下示例所示。

```
select query, substring(filename,22,25) as filename,line_number as line, 
substring(colname,0,12) as column, type, position as pos, substring(raw_line,0,30) as line_text,
substring(raw_field_value,0,15) as field_text, 
substring(err_reason,0,45) as error_reason
from stl_load_errors 
order by query desc, filename 
limit 7;
```

STL\$1LOAD\$1ERRORS 查询的结果应类似于以下内容。

```
 query  |         filename          | line |  column   |    type    | pos |           line_text           | field_text |              error_reason
--------+---------------------------+------+-----------+------------+-----+-------------------------------+------------+----------------------------------------------
 334489 | customer-fw.tbl.log       |    2 | c_custkey | int4       |  -1 | customer-fw.tbl               | customer-f | Invalid digit, Value 'c', Pos 0, Type: Integ
 334489 | customer-fw.tbl.log       |    6 | c_custkey | int4       |  -1 | Complete                      | Complete   | Invalid digit, Value 'C', Pos 0, Type: Integ
 334489 | customer-fw.tbl.log       |    3 | c_custkey | int4       |  -1 | #Total rows                   | #Total row | Invalid digit, Value '#', Pos 0, Type: Integ
 334489 | customer-fw.tbl.log       |    5 | c_custkey | int4       |  -1 | #Status                       | #Status    | Invalid digit, Value '#', Pos 0, Type: Integ
 334489 | customer-fw.tbl.log       |    1 | c_custkey | int4       |  -1 | #Load file                    | #Load file | Invalid digit, Value '#', Pos 0, Type: Integ
 334489 | customer-fw.tbl000        |    1 | c_address | varchar    |  34 | 1         Customer#000000001  | .Mayag.ezR | String contains invalid or unsupported UTF8
 334489 | customer-fw.tbl000        |    1 | c_address | varchar    |  34 | 1         Customer#000000001  | .Mayag.ezR | String contains invalid or unsupported UTF8
(7 rows)
```

通过检查结果，您可以看到 `error_reasons` 列中有以下两条消息：
+ 

  ```
  Invalid digit, Value '#', Pos 0, Type: Integ 
  ```

  这些错误是由 `customer-fw.tbl.log` 文件导致的。问题在于它是一个日志文件而不是数据文件且不应加载。可以使用清单文件来避免加载错误文件。
+ 

  ```
  String contains invalid or unsupported UTF8 
  ```

  VARCHAR 数据类型支持多达 3 个字符的多字节 UTF-8 字符。如果加载数据包含不支持的或无效的字符，则可使用 ACCEPTINVCHARS 选项将每个无效字符替换为指定的替代字符。

另一个加载问题是更难以检测 – 加载生成了意外结果。要调查此问题，请运行以下命令来查询 CUSTOMER 表。

```
select c_custkey, c_name, c_address        
from customer
order by c_custkey
limit 10;
```

```
 c_custkey |          c_name           |         c_address
-----------+---------------------------+---------------------------
         2 | Customer#000000002        | XSTf4,NCwDVaWNe6tE
         2 | Customer#000000002        | XSTf4,NCwDVaWNe6tE
         3 | Customer#000000003        | MG9kdTD
         3 | Customer#000000003        | MG9kdTD
         4 | Customer#000000004        | XxVSJsL
         4 | Customer#000000004        | XxVSJsL
         5 | Customer#000000005        | KvpyuHCplrB84WgAi
         5 | Customer#000000005        | KvpyuHCplrB84WgAi
         6 | Customer#000000006        | sKZz0CsnMD7mp4Xd0YrBvx
         6 | Customer#000000006        | sKZz0CsnMD7mp4Xd0YrBvx
(10 rows)
```

行应是唯一的，但存在重复项。

检查意外结果的另一种方法是验证已加载的行的数量。在我们的示例中，应已加载 100000 行，但加载消息报告已加载 112497 条记录。导致加载了额外的行的原因是，COPY 加载了无关的文件 `customer-fw.tbl0000.bak`。

在本练习中，您将使用清单文件来避免加载错误文件。
<a name="tutorial-loading-acceptinvchars"></a>
**ACCEPTINVCHARS**  
原定设置情况下，在 COPY 遇到列的数据类型不支持的字符时，它会跳过该行并返回错误。有关无效的 UTF-8 字符的信息，请参阅[多字节字符加载错误](multi-byte-character-load-errors.md)。

您可使用 MAXERRORS 选项忽略错误并继续加载，然后查询 STL\$1LOAD\$1ERRORS 以找到无效的字符，并修复数据文件。但是，MAXERRORS 最适合用于解决加载问题，并且通常不应用于生产环境。

ACCEPTINVCHARS 选项通常是用于管理无效字符的更佳选择。ACCEPTINVCHARS 指示 COPY 将每个无效字符替换为指定的有效字符，然后继续加载操作。您可以指定任何有效的 ASCII 字符（NULL 除外）作为替换字符。默认替换字符是问号 ( ? )。COPY 将多字节字符替换为等长的替换字符串。例如，一个 4 字节字符将替换为 `'????'`。

COPY 命令将返回包含无效 UTF-8 字符的行数。它还为每个受影响的行向 STL\$1REPLACEMENTS 系统表添加一个条目，每个节点分片最多添加 100 个行。还将替换其他无效的 UTF-8 字符，但不会记录这些替换事件。

ACCEPTINVCHARS 仅对 VARCHAR 列有效。

在此步骤中，您将使用替换字符 `'^'` 添加 ACCEPTINVCHARS。
<a name="tutorial-loading-manifest"></a>
**MANIFEST**  
当您使用键前缀从 Amazon S3 执行 COPY 命令时，可能会加载不需要的表。例如，`'s3://amzn-s3-demo-bucket/load/` 文件夹包含 8 个数据文件，这些文件共享键前缀 `customer-fw.tbl`：`customer-fw.tbl0000`、`customer-fw.tbl0001` 等。但是，同一文件夹也包含无关的文件：`customer-fw.tbl.log` 和 `customer-fw.tbl-0001.bak`。

要确保您加载所有正确的文件且仅有正确的文件，请使用清单文件。清单是采用 JSON 格式的文本文件，可明确列出要加载的每个源文件的唯一对象键。文件对象可位于不同的文件夹或桶中，但它们必须位于同一区域内。有关更多信息，请参阅 [MANIFEST](copy-parameters-data-source-s3.md#copy-manifest)。

下面显示了 `customer-fw-manifest` 文本。

```
{
  "entries": [
    {"url":"s3://<your-bucket-name>/load/customer-fw.tbl-000"},
    {"url":"s3://<your-bucket-name>/load/customer-fw.tbl-001"},
    {"url":"s3://<your-bucket-name>/load/customer-fw.tbl-002"},
    {"url":"s3://<your-bucket-name>/load/customer-fw.tbl-003"},
    {"url":"s3://<your-bucket-name>/load/customer-fw.tbl-004"},    
    {"url":"s3://<your-bucket-name>/load/customer-fw.tbl-005"},
    {"url":"s3://<your-bucket-name>/load/customer-fw.tbl-006"}, 
    {"url":"s3://<your-bucket-name>/load/customer-fw.tbl-007"} 
    ]
}
```

**使用清单文件加载 CUSTOMER 表的数据**

1. 在文本编辑器中打开文件 `customer-fw-manifest`。

1. 将 *<your-bucket-name>* 替换为您的桶的名称。

1. 保存该文件。

1. 将文件上传到您的桶上的加载文件夹中。

1. 运行以下 COPY 命令。

   ```
   copy customer from 's3://<your-bucket-name>/load/customer-fw-manifest'
   credentials 'aws_iam_role=arn:aws:iam::<aws-account-id>:role/<role-name>' 
   fixedwidth 'c_custkey:10, c_name:25, c_address:25, c_city:10, c_nation:15, c_region :12, c_phone:15,c_mktsegment:10'
   maxerror 10 
   acceptinvchars as '^'
   manifest;
   ```

#### 使用 DATEFORMAT 加载 DWDATE 表
<a name="tutorial-loading-load-dwdate"></a>

在此步骤中，您将使用 DELIMITER 和 DATEFORMAT 选项加载 DWDATE 表。

加载 DATE 和 TIMESTAMP 列时，COPY 应为默认格式，即 YYYY-MM-DD（对于日期）和 YYYY-MM-DD HH:MI:SS（对于时间戳）。如果加载数据不使用默认格式，则可使用 DATEFORMAT 和 TIMEFORMAT 指定格式。

以下摘要显示 DWDATE 表中的日期格式。请注意，列 2 中的日期格式不一致。

```
19920104	1992-01-04          Sunday		January	1992	199201	Jan1992	1	4	4	1...
19920112	January 12, 1992	Monday		January	1992	199201	Jan1992	2	12	12	1...
19920120	January 20, 1992	Tuesday	    January	1992	199201	Jan1992	3	20	20	1...
```
<a name="tutorial-loading-dateformat"></a>
**DATEFORMAT**  
您只能指定一种日期格式。如果加载数据包含不一致的格式（可能位于不同的列中），或者格式在加载时未知，则使用带 `'auto'` 参数的 DATEFORMAT。指定 `'auto'` 后，COPY 命令将识别任何有效的日期或时间格式并将它转换为默认格式。在使用 DATEFORMAT 和 TIMEFORMAT 字符串时，`'auto'` 选项将识别一些不受支持的格式。有关更多信息，请参阅 [在 DATEFORMAT 和 TIMEFORMAT 中使用自动识别](automatic-recognition.md)。

要加载 DWDATE 表，请运行以下 COPY 命令。

```
copy dwdate from 's3://<your-bucket-name>/load/dwdate-tab.tbl'
credentials 'aws_iam_role=arn:aws:iam::<aws-account-id>:role/<role-name>' 
delimiter '\t' 
dateformat 'auto';
```

#### 加载多个数据文件
<a name="tutorial-loading-load-lineorder"></a>

可以使用 GZIP 和 COMPUPDATE 选项来加载表。

可以从单个数据文件或多个文件中加载表。这样做可以比较两种方法的加载时间。
<a name="tutorial-loading-gzip-lzop"></a>
**GZIP、LZOP 和 BZIP2**  
您可以使用 gzip、lzop 或 bzip2 压缩格式压缩您的文件。在从压缩文件加载时，COPY 会在加载过程中解压缩这些文件。压缩文件将节省存储空间并缩短上传时间。
<a name="tutorial-loading-compupdate"></a>
**COMPUPDATE**  
当 COPY 加载无压缩编码的空表时，它会分析加载数据以确定最佳编码。然后，它会修改该表以在开始加载前使用这些编码。此分析过程比较费时，最多对每个表执行一次此过程。要节省时间，您可以通过关闭 COMPUPDATE 来跳过此步骤。为了准确评估 COPY 时间，您将在此步骤中关闭 COMPUPDATE。
<a name="tutorial-loading-multiple-files"></a>
**多个文件**  
COPY 命令可在从多个文件并行加载（而不是从一个文件加载）时非常高效地加载数据。您可以将数据拆分成多个文件，以便文件数是您的集群中的切片数的倍数。这样，Amazon Redshift 将划分工作负载并在切片之间均匀分配数据。每个节点的切片数取决于集群的节点大小。有关每个节点大小拥有的切片数的更多信息，请转到《Amazon Redshift 管理指南》**中的[关于集群和节点](https://docs.aws.amazon.com/redshift/latest/mgmt/working-with-clusters.html#rs-about-clusters-and-nodes)。

例如，本教程的集群中使用的计算节点可以各有两个切片，因此 4 节点集群具有 8 个切片。在前面的步骤中，加载数据已包含在 8 个文件中，尽管这些文件非常小。您可以比较从单个大文件加载和从多个文件加载之间的时间差异。

即使是包含 1500 万条记录并占用约 1.2 GB 的文件，对于 Amazon Redshift 规模而言也是很小的。但它们足以展示从多个文件加载的性能优势。

下图显示 LINEORDER 的数据文件。

![\[LINEORDER 表中的数据分为九个文件。\]](http://docs.aws.amazon.com/zh_cn/redshift/latest/dg/images/tutorial-load-lineorder-files.png)


**使用多个文件评估 COPY 的性能**

1. 在实验室测试中，运行了以下命令以从单个文件进行 COPY。此命令显示了一个虚构的存储桶。

   ```
   copy lineorder from 's3://amzn-s3-demo-bucket/load/lo/lineorder-single.tbl' 
   credentials 'aws_iam_role=arn:aws:iam::<aws-account-id>:role/<role-name>' 
   gzip
   compupdate off
   region 'us-east-1';
   ```

1. 结果如下所示。请注意执行时间。

   ```
   Warnings:
   Load into table 'lineorder' completed, 14996734 record(s) loaded successfully.
   
   0 row(s) affected.
   copy executed successfully
   
   Execution time: 51.56s
   ```

1. 然后，运行以下命令来从多个文件进行 COPY。

   ```
   copy lineorder from 's3://amzn-s3-demo-bucket/load/lo/lineorder-multi.tbl' 
   credentials 'aws_iam_role=arn:aws:iam::<aws-account-id>:role/<role-name>' 
   gzip
   compupdate off
   region 'us-east-1';
   ```

1. 结果如下所示。请注意执行时间。

   ```
   Warnings:
   Load into table 'lineorder' completed, 14996734 record(s) loaded successfully.
   
   0 row(s) affected.
   copy executed successfully
   
   Execution time: 17.7s
   ```

1. 比较执行时间。

   在我们的实验中，加载 1500 万条记录所需的时间已从 51.56 秒减少至 17.7 秒（减少了 65.7%）。

   这些结果通过使用 4 节点集群获得。如果您的集群具有更多节点，则将加倍节省时间。对于具有数十至数百个节点的典型 Amazon Redshift 集群，差异甚至更大。如果您有一个单节点集群，则执行时间之间的差异很小。

## 步骤 6：对数据库执行 vacuum 和分析操作
<a name="tutorial-loading-data-vacuum"></a>

当添加、删除或修改大量行时，您应运行 VACUUM 命令，然后运行 ANALYZE 命令。*vacuum* 将从已删除行中恢复空间并还原排序顺序。ANALYZE 命令更新统计元数据，这使查询优化程序能够生成更准确的查询计划。有关更多信息，请参阅 [对表执行 vacuum 操作](t_Reclaiming_storage_space202.md)。

如果您按排序键顺序加载数据，则 vacuum 操作的速度会很快。在本教程中，虽然您添加了大量行，但这些行添加到了空表中。在这种情况下，无需重新排序，而且您不删除任何行。COPY 在加载空表后自动更新统计数据，因此您的统计数据应是最新的。但是，要实现出色的事务管理，您需要通过对数据库执行 vacuum 和分析操作来完成本教程。

要对数据库执行 vacuum 和分析操作，请运行以下命令。

```
vacuum;
analyze;
```

## 步骤 7：清理资源
<a name="tutorial-loading-data-clean-up"></a>

只要您的集群正在运行，就将继续产生费用。完成本教程后，您应按照《Amazon Redshift 入门指南》**中的[步骤 5：撤消访问权限并删除示例集群](https://docs.aws.amazon.com/redshift/latest/gsg/rs-gsg-clean-up-tasks.html)中的步骤操作，以使您的环境返回上一个状态。

如果您希望保留集群，但恢复 SSB 表使用的存储空间，请运行以下命令。

```
drop table part;
drop table supplier;
drop table customer;
drop table dwdate;
drop table lineorder;
```

### 下一步
<a name="tutorial-loading-next-summary"></a>

[摘要](#tutorial-loading-data-summary)

## 摘要
<a name="tutorial-loading-data-summary"></a>

在本教程中，您已将数据文件上载到 Amazon S3，然后已使用 COPY 命令将数据从文件加载到 Amazon Redshift 表中。

您已使用以下格式加载数据：
+ 字符分隔的
+ CSV
+ 固定宽度

您使用了 STL\$1LOAD\$1ERRORS 系统表来针对加载错误，然后使用了 REGION、MANIFEST、MAXERROR、ACCEPTINVCHARS、DATEFORMAT 和 NULL AS 选项纠正这些错误。

您应用了加载数据的以下最佳实践：
+ [使用 COPY 命令加载数据](c_best-practices-use-copy.md)
+ [加载数据文件](c_best-practices-use-multiple-files.md)
+ [使用一个 COPY 命令从多个文件中加载](c_best-practices-single-copy-command.md)
+ [压缩数据文件](c_best-practices-compress-data-files.md)
+ [在加载前后验证数据文件](c_best-practices-verifying-data-files.md)

有关 Amazon Redshift 最佳实践的更多信息，请参阅以下链接：
+ [Amazon Redshift 加载数据的最佳实践](c_loading-data-best-practices.md)
+ [设计表的 Amazon Redshift 最佳实践](c_designing-tables-best-practices.md) 
+ [设计查询的 Amazon Redshift 最佳实践](c_designing-queries-best-practices.md) 