

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 在 Amazon EMR 中使用物化视图
<a name="emr-spark-materialized-views"></a>

亚马逊 EMR 7.12.0 及更高版本支持在 Glue 数据目录中创建和管理 Apache Iceberg 物化视图。 AWS 实体化视图是一种托管表，该表以 Apache Iceberg 格式存储 SQL 查询预先计算的结果，并随着基础源表的更改而增量更新。可以使用实体化视图来简化数据转换管道并提高复杂分析工作负载的查询性能。

当您在 Amazon EMR 上使用 Spark 创建物化视图时，视图定义和元数据存储在 AWS Glue 数据目录中。预先计算的结果以 Apache Iceberg 表的形式存储在您的账户中的 Amazon S3 表存储桶或一般用途存储桶中的 Amazon S3 通用存储桶中。 AWS AWS Glue 数据目录使用托管计算基础架构自动监控源表并刷新物化视图。

**Topics**
+ [物化视图如何与 Amazon EMR 配合使用](#emr-spark-materialized-views-how-it-works)
+ [先决条件](#emr-spark-materialized-views-prerequisites)
+ [将 Spark 配置为使用实体化视图](#emr-spark-materialized-views-configure)
+ [创建实体化视图](#emr-spark-materialized-views-create)
+ [查询实体化视图](#emr-spark-materialized-views-query)
+ [刷新实体化视图](#emr-spark-materialized-views-refresh)
+ [管理实体化视图](#emr-spark-materialized-views-manage)
+ [实体化视图的权限](#emr-spark-materialized-views-permissions)
+ [监控实体化视图操作](#emr-spark-materialized-views-monitoring)
+ [示例：完整工作流程](#emr-spark-materialized-views-example)
+ [注意事项和限制](#emr-spark-materialized-views-limitations)

## 物化视图如何与 Amazon EMR 配合使用
<a name="emr-spark-materialized-views-how-it-works"></a>

通过 Apache Spark 的 Iceberg 支持，物化视图与 Amazon EMR 集成。将 Spark 会话配置为使用 AWS Glue 数据目录时，您可以使用标准 SQL 语法创建物化视图。当实体化视图提供更好的性能时，Spark 优化器可以自动重写查询进而使用这些视图，从而无需手动修改应用程序代码。

 AWS Glue 数据目录处理物化视图维护的所有操作方面，包括：
+ 使用 Apache Iceberg 的元数据层检测源表中的更改
+ 使用托管的 Spark 计算计划和执行刷新操作
+ 根据数据更改确定是执行完全刷新还是增量刷新
+ 以 Apache Iceberg 格式存储预先计算的结果，便于多引擎访问

您可以使用与常规表相同的 Spark SQL 接口从 Amazon EMR 中查询物化视图。也可以从其他服务访问预先计算的数据，包括 Amazon Athena 和 Amazon Redshift。

## 先决条件
<a name="emr-spark-materialized-views-prerequisites"></a>

要在 Amazon EMR 中使用物化视图，您需要：
+ 一个 AWS 账户
+ 运行 7.12.0 或更高版本的 Amazon EMR 集群
+ 在 Glue 数据目录中注册的 Apache Iceberg 格式的 AWS 源表
+ AWS 为源表和目标数据库配置的 Lake Formation 权限
+ 在 La AWS ke Formation 中注册的 S3 表存储桶或 S3 通用存储桶，用于存储物化视图数据

## 将 Spark 配置为使用实体化视图
<a name="emr-spark-materialized-views-configure"></a>

要创建和管理物化视图，请使用所需的 Iceberg 扩展和目录设置来配置 Spark 会话。配置会有所不同，具体取决于您的源表和实例化视图使用 S3 表存储桶还是 S3 通用存储桶。

### 为 S3 表进行配置
<a name="emr-spark-materialized-views-configure-s3-tables"></a>

使用 S3 Tables 存储桶作为物化视图时，请为源表和实例化视图配置单独的目录引用：

```
spark-sql \
  --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions \
  --conf spark.sql.catalog.glue_catalog=org.apache.iceberg.spark.SparkCatalog \
  --conf spark.sql.catalog.glue_catalog.type=glue \
  --conf spark.sql.catalog.glue_catalog.warehouse=s3://amzn-s3-demo-bucket/warehouse \
  --conf spark.sql.catalog.glue_catalog.glue.region=us-east-1 \
  --conf spark.sql.catalog.glue_catalog.glue.id=111122223333 \
  --conf spark.sql.catalog.glue_catalog.glue.account-id=111122223333 \
  --conf spark.sql.catalog.glue_catalog.glue.lakeformation-enabled=true \
  --conf spark.sql.catalog.s3t_catalog=org.apache.iceberg.spark.SparkCatalog \
  --conf spark.sql.catalog.s3t_catalog.type=glue \
  --conf spark.sql.catalog.s3t_catalog.glue.id=111122223333:s3tablescatalog/my-table-bucket \
  --conf spark.sql.catalog.s3t_catalog.glue.account-id=111122223333 \
  --conf spark.sql.catalog.s3t_catalog.glue.lakeformation-enabled=true \
  --conf spark.sql.catalog.s3t_catalog.warehouse=s3://amzn-s3-demo-bucket/mv-warehouse \
  --conf spark.sql.catalog.s3t_catalog.glue.region=us-east-1 \
  --conf spark.sql.defaultCatalog=s3t_catalog \
  // turn on automatic query rewrite (optional)
  --conf spark.sql.optimizer.answerQueriesWithMVs.enabled=true
```

### 为 S3 通用存储桶进行配置
<a name="emr-spark-materialized-views-configure-s3-general"></a>

使用 S3 通用存储桶时，请配置单个目录引用：

```
spark-sql \
  --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions \
  --conf spark.sql.catalog.glue_catalog=org.apache.iceberg.spark.SparkCatalog \
  --conf spark.sql.catalog.glue_catalog.type=glue \
  --conf spark.sql.catalog.glue_catalog.warehouse=s3://amzn-s3-demo-bucket/warehouse \
  --conf spark.sql.catalog.glue_catalog.glue.region=us-east-1 \
  --conf spark.sql.catalog.glue_catalog.glue.id=111122223333 \
  --conf spark.sql.catalog.s3t_catalog.glue.account-id=111122223333 \
  --conf spark.sql.catalog.s3t_catalog.glue.lakeformation-enabled=true \
  --conf spark.sql.defaultCatalog=glue_catalog \
  // turn on automatic query rewrite (optional)
  --conf spark.sql.optimizer.answerQueriesWithMVs.enabled=true
```

### 启用增量刷新
<a name="emr-spark-materialized-views-configure-incremental"></a>

要启用增量刷新优化，请在 Spark 会话中添加以下配置属性：

```
spark-sql \
  --conf spark.sql.optimizer.incrementalMVRefresh.enabled=true \
```

### 配置参数
<a name="emr-spark-materialized-views-configure-parameters"></a>

以下配置参数控制实体化视图的行为：
+ `spark.sql.extensions` – 启用支持实体化视图所需的 Iceberg Spark 会话扩展。
+ `spark.sql.optimizer.answerQueriesWithMVs.enabled` – 启用自动查询重写，进而使用实体化视图 设置为 true 可激活此优化。
+ `spark.sql.optimizer.incrementalMVRefresh.enabled` – 启用增量刷新优化。设置为 true 可在刷新操作期间仅处理更改的数据。

## 创建实体化视图
<a name="emr-spark-materialized-views-create"></a>

您可以使用创建物化视图 SQL 语句创建实例化视图。视图定义将转换逻辑指定为引用一个或多个源表的 SQL 查询。

### DLLs
<a name="emr-spark-materialized-views-create-dlls"></a>

**创建视图**

```
{ CREATE OR REPLACE MATERIALIZED VIEW | CREATE MATERIALIZED VIEW [ IF NOT EXISTS ] }
   view_identifier
  [ view_clauses ]
  [ schedule_clauses ]
  AS [ select_statement ]

view_clauses =
  { [ LOCATION location ] |
    [ PARTITIONED BY (col [, ...]) ] |
    [ COMMENT view_comment ] |
    [ SCHEDULE [ REFRESH ] schedule_clause ] }

schedule_clause =
  { EVERY number { HOUR | HOURS | DAY | DAYS | WEEK | WEEKS } }
```

**注意**  
视图子句必须出现在选择语句之前。

### 创建基本的物化视图
<a name="emr-spark-materialized-views-create-basic"></a>

以下示例创建了一个按客户聚合订单数据的物化视图，在视图定义中使用具有三部分命名约定的完全限定表名：

```
CREATE MATERIALIZED VIEW customer_orders
AS 
SELECT 
    customer_name, 
    COUNT(*) as order_count, 
    SUM(amount) as total_amount 
FROM glue_catalog.sales.orders
GROUP BY customer_name;
```

### 创建具有自动刷新功能的实体化视图
<a name="emr-spark-materialized-views-create-auto-refresh"></a>

要配置自动刷新，请在创建视图时使用完全限定的表名指定刷新计划，视图定义中有三部分命名约定：

```
CREATE MATERIALIZED VIEW customer_orders
SCHEDULE REFRESH EVERY 1 HOUR
AS 
SELECT 
    customer_name, 
    COUNT(*) as order_count, 
    SUM(amount) as total_amount 
FROM glue_catalog.sales.orders
GROUP BY customer_name;
```

### 创建具有跨目录引用的实体化视图
<a name="emr-spark-materialized-views-create-cross-catalog"></a>

当源表与实体化视图位于不同的目录中时，请在视图名称和视图定义中使用具有三部分命名约定的完全限定表名：

```
CREATE MATERIALIZED VIEW s3t_catalog.analytics.customer_summary
AS 
SELECT 
    customer_name, 
    COUNT(*) as order_count, 
    SUM(amount) as total_amount 
FROM glue_catalog.sales.orders
GROUP BY customer_name;
```

## 查询实体化视图
<a name="emr-spark-materialized-views-query"></a>

创建实例化视图后，您可以像使用标准的 SQL SELECT 语句一样查询该视图：

```
SELECT * FROM customer_orders;
```

### 自动查询重写
<a name="emr-spark-materialized-views-query-rewrite"></a>

启用自动查询重写后，Spark 优化器会分析您的查询，并在实体化视图可以提高性能时自动使用这些视图。例如，如果执行以下查询：

```
SELECT 
    customer_name, 
    COUNT(*) as order_count, 
    SUM(amount) as total_amount 
FROM orders
GROUP BY customer_name;
```

只要实体化视图是最新的，Spark 优化器就会自动重写此查询，进而使用 customer\$1orders 实体化视图而不是处理基本订单表。

### 验证自动查询重写
<a name="emr-spark-materialized-views-query-verify"></a>

要验证查询是否使用自动查询重写，请使用 EXPLAIN EXTENDED 命令：

```
EXPLAIN EXTENDED
SELECT customer_name, COUNT(*) as order_count, SUM(amount) as total_amount 
FROM orders
GROUP BY customer_name;
```

在执行计划中，在 BatchScan操作中查找实例化视图的名称。如果计划显示 g BatchScan lue\$1catalog.analytics.customer\$1orders 而不是 glue\$1catalog.s BatchScan ales.orders，则查询已被自动重写为使用实体化视图。

**注意**  
创建物化视图后，自动查询重写需要一段时间才能填充 Spark 元数据缓存。该过程通常在 30 秒内完成。

## 刷新实体化视图
<a name="emr-spark-materialized-views-refresh"></a>

可以使用两种方法刷新实体化视图：完全刷新或增量刷新。完全刷新会根据所有基表数据重新计算整个实体化视图，而增量刷新仅处理自上次刷新以来发生更改的数据。

### 手动完全刷新
<a name="emr-spark-materialized-views-refresh-full"></a>

要对实体化视图进行完全刷新，请执行以下操作：

```
REFRESH MATERIALIZED VIEW customer_orders FULL;
```

执行此命令后，查询实例化视图以验证更新的结果：

```
SELECT * FROM customer_orders;
```

### 手动增量刷新
<a name="emr-spark-materialized-views-refresh-incremental"></a>

要进行增量刷新，请确保在 Spark 会话配置中启用增量刷新，然后执行：

```
REFRESH MATERIALIZED VIEW customer_orders;
```

 AWS Glue 数据目录会根据视图定义和更改的数据量自动确定增量刷新是否适用。如果无法进行增量刷新，则操作将回退为完全刷新。

### 验证增量刷新执行情况
<a name="emr-spark-materialized-views-refresh-verify"></a>

要确认增量刷新已成功执行，您可以通过运行以下命令来检查`lastRefreshType`表的属性：

```
SHOW TBLPROPERTIES <mvName>("lastRefreshType")
```

此外，这也可以通过修改 Spark 日志配置来启用调试日志记录来实现：

1. 打开 Spark log4j 配置文件：

   ```
   sudo vim /usr/lib/spark/conf/log4j2.properties
   ```

1. 添加以下记录器配置：

   ```
   logger.spark.name = org.apache.spark.sql
   logger.spark.level = debug
   
   logger.inmemcache.name = org.apache.spark.sql.InMemMvMetadataCache
   logger.inmemcache.level = off
   ```

1. 执行刷新操作后，在 Spark 输出中搜索以下消息：

   ```
   DEBUG RefreshMaterializedViewExec: Executed Incremental Refresh
   ```

## 管理实体化视图
<a name="emr-spark-materialized-views-manage"></a>

Amazon EMR 提供了用于管理物化视图生命周期的 SQL 命令。

### 描述实体化视图
<a name="emr-spark-materialized-views-manage-describe"></a>

要查看有关实体化视图的元数据，包括其定义、刷新状态和上次刷新时间戳，请执行以下操作：

```
DESCRIBE EXTENDED customer_orders;
```

### 更改实体化视图
<a name="emr-spark-materialized-views-manage-alter"></a>

要修改现有实体化视图的刷新计划，请执行以下操作：

```
ALTER MATERIALIZED VIEW customer_orders 
ADD SCHEDULE REFRESH EVERY 2 HOURS;
```

要移除自动刷新，请执行以下操作：

```
ALTER MATERIALIZED VIEW customer_orders 
DROP SCHEDULE;
```

### 删除实体化视图
<a name="emr-spark-materialized-views-manage-drop"></a>

要删除实体化视图，请执行以下操作：

```
DROP MATERIALIZED VIEW customer_orders;
```

此命令从 AWS Glue 数据目录中删除实例化视图定义，并从 S3 存储桶中删除底层 Iceberg 表数据。

## 实体化视图的权限
<a name="emr-spark-materialized-views-permissions"></a>

要创建和管理物化视图，必须配置 AWS Lake Formation 权限。创建实体化视图的 IAM 角色（定义者角色）需要对源表和目标数据库的特定权限。

### 定义者角色所需的权限
<a name="emr-spark-materialized-views-permissions-definer"></a>

定义者角色必须具有以下 Lake Formation 权限：
+ 在源表上 – 不带行、列或单元格筛选条件的 SELECT 或 ALL 权限
+ 在目标数据库上 – CREATE\$1TABLE 权限
+ 在 AWS Glue 数据目录上 — GetTable 以及 CreateTable API 权限

创建实体化视图时，定义者角色的 ARN 存储在视图定义中。 AWS Glue 数据目录在执行自动刷新操作时扮演此角色。如果定义者角色失去对源表的访问权限，则在恢复权限之前，刷新操作将失败。

### 授予对实体化视图的访问权限
<a name="emr-spark-materialized-views-permissions-access"></a>

要向其他用户授予查询物化视图的权限，请使用 AWS Lake Formation 授予对物化视图表的 SELECT 权限。用户无需直接访问基础源表即可查询实体化视图。

有关配置 Lake Formation 权限的详细信息，请参阅 La AWS ke Formation 开发者指南中的授予和撤消数据目录资源的权限。

## 监控实体化视图操作
<a name="emr-spark-materialized-views-monitoring"></a>

 AWS Glue 数据目录将物化视图刷新操作的指标和日志发布到 Amazon CloudWatch。您可以监控刷新状态、持续时间和通过 CloudWatch 指标处理的数据量。

### 查看刷新指标
<a name="emr-spark-materialized-views-monitoring-metrics"></a>

要查看实体化视图刷新度量，请执行以下操作：

1. 打开控制 CloudWatch 台。

1. 从导航窗格中选择 “指标”。

1. 选择 Glue 命名空间。

1. 按物化视图名称筛选指标。

### 设置警报
<a name="emr-spark-materialized-views-monitoring-alarms"></a>

要在刷新操作失败或超过预期持续时间时收到通知，请针对物化视图指标创建 CloudWatch 警报。您还可以配置 Amazon EventBridge 规则以触发对刷新事件的自动响应。

## 示例：完整工作流程
<a name="emr-spark-materialized-views-example"></a>

以下示例演示了在 Amazon EMR 上创建和使用物化视图的完整工作流程。

1. 使用 SSH 连接到您的 EMR 集群主节点。

1. 使用示例数据创建基表：

   ```
   spark-sql \
     --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions \
     --conf spark.sql.catalog.glue_catalog=org.apache.iceberg.spark.SparkCatalog \
     --conf spark.sql.catalog.glue_catalog.type=glue \
     --conf spark.sql.catalog.glue_catalog.warehouse=s3://amzn-s3-demo-bucket/warehouse \
     --conf spark.sql.catalog.glue_catalog.glue.region=us-east-1 \
     --conf spark.sql.catalog.glue_catalog.glue.id=111122223333 \
     --conf spark.sql.catalog.glue_catalog.glue.account-id=111122223333 \
     --conf spark.sql.catalog.glue_catalog.glue.lakeformation-enabled=true \
     --conf spark.sql.defaultCatalog=glue_catalog \
     --conf spark.sql.optimizer.answerQueriesWithMVs.enabled=true 
   
   
   CREATE DATABASE IF NOT EXISTS sales;
   
   USE sales;
   
   CREATE TABLE orders (
       id INT,
       customer_name STRING,
       amount DECIMAL(10,2),
       order_date DATE
   );
   
   INSERT INTO orders VALUES 
       (1, 'John Doe', 150.00, DATE('2024-01-15')),
       (2, 'Jane Smith', 200.50, DATE('2024-01-16')),
       (3, 'Bob Johnson', 75.25, DATE('2024-01-17'));
   ```

1. 创建物化视图：

   ```
   CREATE MATERIALIZED VIEW customer_summary
   AS 
   SELECT 
       customer_name, 
       COUNT(*) as order_count, 
       SUM(amount) as total_amount 
   FROM glue_catalog.sales.orders
   GROUP BY customer_name;
   ```

1. 查询物化视图：

   ```
   SELECT * FROM customer_summary;
   ```

1. 在基表中插入其他数据：

   ```
   INSERT INTO orders VALUES 
       (4, 'Jane Smith', 350.00, DATE('2024-01-18')),
       (5, 'Bob Johnson', 100.25, DATE('2024-01-19'));
   ```

1. 刷新实体化视图：

   ```
   REFRESH MATERIALIZED VIEW customer_summary FULL;
   ```

1. 验证更新的结果：

   ```
   SELECT * FROM customer_summary;
   ```

## 注意事项和限制
<a name="emr-spark-materialized-views-limitations"></a>

在 Amazon EMR 中使用物化视图时，请考虑以下几点：
+ 物化视图要求使用 Amazon EMR 7.12.0 或更高版本。
+ 源表必须是在 G AWS lue 数据目录中注册的 Apache Iceberg 表。Apache Hive、Apache Hudi 和 Linux Foundation Delta Lake 表在发布时不受支持。
+ 源表必须与物化视图位于相同的 AWS 区域和 AWS 帐户中。
+ 所有源表都必须由 AWS Lake Formation 管理。不支持仅限 IAM 权限和混合访问权限。
+ 物化视图不能将 AWS Glue 数据目录视图、多方言视图或其他物化视图引用为源表。
+ 视图定义者角色必须对所有未应用行、列或单元格筛选条件的源表具有完全读取权限（SELECT 或 ALL 权限）。
+ 实体化视图最终与源表保持一致。在刷新窗口期间，查询可能会返回过时数据。执行手动刷新，立即保持一致性。
+ 最小自动刷新间隔为一小时。
+ 增量刷新支持有限的 SQL 操作子集。视图定义必须是单个 B SELECT-FROM-WHERE-GROUP Y-HAVING 块，并且不能包含集合操作、子查询、SELECT 或聚合函数中的 DISTINCT 关键字、窗口函数或 INNER JOIN 以外的联接。
+ 增量刷新不支持用户定义的函数或某些内置函数。仅支持 Spark SQL 内置函数的子集。
+ 查询自动重写仅考虑其定义属于受限 SQL 子集的实体化视图，类似于增量刷新限制。
+ 完全刷新操作会覆盖整个表，并使以前的快照不可用。
+ CREATE MATERIALIZED VIEW 查询中不支持包含除字母数字字符和下划线之外的特殊字符的标识符。
+ 以 \$1\$1ivm 前缀开头的实例化视图列保留供系统使用。 AWS 保留在 future 版本中修改或删除这些列的权利。
+ 实体化视图定义中不支持 SORT BY、LIMIT、OFFSET、CLUSTER BY 和 ORDER BY 子句。
+ 不支持跨区域和跨账户源表。
+ 实体化视图定义中不支持诸如 as rand() 或 current\$1timestamp() 之类的非确定性函数。