

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

# EMR Serverless 的 Lake Formation 完整表访问权限
<a name="lake-formation-unfiltered-access"></a>

在 Amazon EMR 7.8.0 及更高版本中，您可以利用带有 Glue Data Catalog 的 Lake Formation AWS ，其中任务运行时角色拥有完整的表权限，不受细粒度访问控制的限制。此功能允许您从 EMR Serverless Spark 批处理和交互式作业中读取和写入受 Lake Formation 保护的表。请参阅以下部分，了解有关 Lake Formation 以及如何将其与 EMR Serverless 结合使用的更多信息。

## 对 Lake Formation 使用全表访问
<a name="lake-formation-unfiltered-full-access"></a>

您可以从 EMR Serverless Spark 作业或交互式会话中访问受 La AWS ke Formation 保护的 Glue 数据目录表，在这些会话中，作业的运行时角色具有完整表访问权限。您无需在 EMR 无服务器应用程序上启用 AWS Lake Formation。将 Spark 作业配置为全表访问权限 (FTA) 时， AWS Lake Formation 凭据将用于 La AWS ke Formati read/write on 注册表的 S3 数据，而作业的运行时角色凭据将用于未在 AWS Lake Formation 中注册的 read/write 表。

**重要**  
请勿启用 AWS Lake Formation 进行精细访问控制。在同一个 EMR 集群或应用程序上，作业不能同时运行全表访问 (FTA) 和精细访问控制 (FGAC)。

### 步骤 1：在 Lake Formation 中启用全表访问
<a name="lake-formation-unfiltered-full-table-access"></a>

要使用全表访问 (FTA) 模式，您必须允许第三方查询引擎访问数据，而无需在 La AWS ke Formation 中进行 IAM 会话标签验证。要启用此模式，请按照 [Application integration for full table access](https://docs.aws.amazon.com/lake-formation/latest/dg/full-table-credential-vending.html) 中的步骤操作。

**注意**  
 访问跨账户表时，必须在生产者账户和消费者账户中启用全表访问。同样，在访问跨区域表时，必须在生产者和使用者区域中都启用此设置。

### 第 2 步：设置作业运行时角色的 IAM 权限
<a name="lake-formation-unfiltered-iam-permissions"></a>

要获得基础数据的读取或写入权限，除 Lake Formation 权限外，作业运行时角色还需要具有 `lakeformation:GetDataAccess` IAM 权限。获得此权限后，Lake Formation 将授权访问数据的临时凭证请求。

下面是一个策略示例，展示了如何提供 IAM 权限以访问 Amazon S3 中的脚本、将日志上传到 S3、 AWS Glue API 权限以及访问 Lake Formation 的权限。

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

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "ScriptAccess",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::*.amzn-s3-demo-bucket/scripts"
      ]
    },
    {
      "Sid": "LoggingAccess",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::amzn-s3-demo-bucket/logs/*"
      ]
    },
    {
      "Sid": "GlueCatalogAccess",
      "Effect": "Allow",
      "Action": [
        "glue:Get*",
        "glue:Create*",
        "glue:Update*"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "LakeFormationAccess",
      "Effect": "Allow",
      "Action": [
        "lakeformation:GetDataAccess"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
```

------

#### 步骤 2.1：配置 Lake Formation 权限
<a name="lake-formation-unfiltered-permission-model"></a>
+ 从 S3 读取数据的 Spark 作业需要 Lake Formation SELECT 权限。
+ 在 S3 中输入 write/delete 数据的 Spark 任务需要 Lake Formation ALL（超级）权限。
+ 与 Glue Data Catalog 交互的 Spark 作业需要视情况具有 DESCRIBE、ALTER、DROP 权限。

有关更多信息，请参阅[授予对 Data Catalog 资源的权限](https://docs.aws.amazon.com/lake-formation/latest/dg/granting-catalog-permissions.html)。

### 步骤 3：使用 Lake Formation 初始化 Spark 会话以实现全表访问
<a name="lake-formation-unfiltered-spark-session"></a>

#### 先决条件
<a name="lake-formation-unfiltered-spark-session-prereq"></a>

AWS 必须将 Glue 数据目录配置为元数据仓才能访问 Lake Formation 表。

设置以下设置以将 Glue 目录配置为元存储：

```
--conf spark.sql.catalogImplementation=hive
--conf spark.hive.metastore.client.factory.class=com.amazonaws.glue.catalog.metastore.AWSGlueDataCatalogHiveClientFactory
```

有关为 EMR Serverless 启用 Data Catalog 的更多信息，请参阅 [EMR Serverless 的元存储配置](metastore-config.html)。

要访问在 AWS Lake Formation 中注册的表，需要在 Spark 初始化期间设置以下配置，将 Spark 配置为使用 AWS Lake Formation 凭据。

------
#### [ Hive ]

```
‐‐conf spark.hadoop.fs.s3.credentialsResolverClass=com.amazonaws.glue.accesscontrol.AWSLakeFormationCredentialResolver
--conf spark.hadoop.fs.s3.useDirectoryHeaderAsFolderObject=true 
--conf spark.hadoop.fs.s3.folderObject.autoAction.disabled=true
--conf spark.sql.catalog.skipLocationValidationOnCreateTable.enabled=true
--conf spark.sql.catalog.createDirectoryAfterTable.enabled=true
--conf spark.sql.catalog.dropDirectoryBeforeTable.enabled=true
```

------
#### [ Iceberg ]

```
--conf spark.sql.catalog.spark_catalog=org.apache.iceberg.spark.SparkSessionCatalog
--conf spark.sql.catalog.spark_catalog.warehouse={{S3_DATA_LOCATION}}
--conf spark.sql.catalog.spark_catalog.client.region={{REGION}}
--conf spark.sql.catalog.spark_catalog.type=glue
--conf spark.sql.catalog.spark_catalog.glue.account-id={{ACCOUNT_ID}}
--conf spark.sql.catalog.spark_catalog.glue.lakeformation-enabled=true
--conf spark.sql.catalog.dropDirectoryBeforeTable.enabled=true
```

------
#### [ Delta Lake ]

```
‐‐conf spark.hadoop.fs.s3.credentialsResolverClass=com.amazonaws.glue.accesscontrol.AWSLakeFormationCredentialResolver
--conf spark.hadoop.fs.s3.useDirectoryHeaderAsFolderObject=true 
--conf spark.hadoop.fs.s3.folderObject.autoAction.disabled=true
--conf spark.sql.catalog.skipLocationValidationOnCreateTable.enabled=true
--conf spark.sql.catalog.createDirectoryAfterTable.enabled=true
--conf spark.sql.catalog.dropDirectoryBeforeTable.enabled=true
```

------
#### [ Hudi ]

```
‐‐conf spark.hadoop.fs.s3.credentialsResolverClass=com.amazonaws.glue.accesscontrol.AWSLakeFormationCredentialResolver
--conf spark.hadoop.fs.s3.useDirectoryHeaderAsFolderObject=true 
--conf spark.hadoop.fs.s3.folderObject.autoAction.disabled=true
--conf spark.sql.catalog.skipLocationValidationOnCreateTable.enabled=true
--conf spark.sql.catalog.createDirectoryAfterTable.enabled=true
--conf spark.sql.catalog.dropDirectoryBeforeTable.enabled=true
--conf spark.jars=/usr/lib/hudi/hudi-spark-bundle.jar
--conf spark.sql.extensions=org.apache.spark.sql.hudi.HoodieSparkSessionExtension
--conf spark.sql.catalog.spark_catalog=org.apache.spark.sql.hudi.catalog.HoodieCatalog
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer
```

------
+ `spark.hadoop.fs.s3.credentialsResolverClass=com.amazonaws.glue.accesscontrol.AWSLakeFormationCredentialResolver`：将 EMR 文件系统 (EMRFS) 或 EMR S3A 配置为对 Lake Formation 注册表使用 L AWS ake Formation S3 凭据。如果表未注册，请使用作业的运行时角色凭证。
+ `spark.hadoop.fs.s3.useDirectoryHeaderAsFolderObject=true` 和 `spark.hadoop.fs.s3.folderObject.autoAction.disabled=true`：在创建 S3 文件夹时，将 EMRFS 配置为使用内容类型标头应用程序/x 目录，而不是 $folder$ 后缀。这在读取 Lake Formation 表时为必需，因为 Lake Formation 凭证不允许读取带有 $folder$ 后缀的表文件夹。
+ `spark.sql.catalog.skipLocationValidationOnCreateTable.enabled=true`：将 Spark 配置为在创建表之前跳过空表位置验证。这对于注册到 Lake Formation 的表是必需的，因为只有在创建 Glue 数据目标表之后，用于验证空位置的 Lake Formation 凭证才会可用。如果未启用此配置，则作业的运行时角色凭证将会验证空表位置。
+ `spark.sql.catalog.createDirectoryAfterTable.enabled=true`：将 Spark 配置为在 Hive 元数据存储中创建表后再创建 Amazon S3 文件夹。这对于注册到 Lake Formation 的表为必需，因为只有在创建 Glue Data Catalog 表之后，用于创建 S3 文件夹的 Lake Formation 凭证才会可用。
+ `spark.sql.catalog.dropDirectoryBeforeTable.enabled=true`：将 Spark 配置为在 Hive 元数据存储中删除表之前删除 S3 文件夹。这对于注册到 Lake Formation 的表是必需的，因为在从 Glue Data Catalog 中删除表之后，用于删除 S3 文件夹的 Lake Formation 凭证将不可用。
+ `spark.sql.catalog.<catalog>.glue.lakeformation-enabled=true`: 将 Iceberg 目录配置为使用 AWS Lake Formation S3 凭据对 Lake Formation 注册的表。如果表未注册，则会使用默认环境凭证。

#### 在 SageMaker 统一工作室中配置全桌访问模式
<a name="lake-formation-unfiltered-full-table"></a>

要通过 JupyterLab 笔记本中的交互式 Spark 会话访问 Lake Formation 注册的表，请使用兼容权限模式。使用 %%configure 魔术命令设置 Spark 配置。根据表类型选择配置：

------
#### [ For Hive tables ]

```
%%configure -f
{
    "conf": {
        "spark.hadoop.fs.s3.credentialsResolverClass": "com.amazonaws.glue.accesscontrol.AWSLakeFormationCredentialResolver",
        "spark.hadoop.fs.s3.useDirectoryHeaderAsFolderObject": true,
        "spark.hadoop.fs.s3.folderObject.autoAction.disabled": true,
        "spark.sql.catalog.skipLocationValidationOnCreateTable.enabled": true,
        "spark.sql.catalog.createDirectoryAfterTable.enabled": true,
        "spark.sql.catalog.dropDirectoryBeforeTable.enabled": true
    }
}
```

------
#### [ For Iceberg tables ]

```
%%configure -f
{
    "conf": {
        "spark.sql.catalog.spark_catalog": "org.apache.iceberg.spark.SparkSessionCatalog",
        "spark.sql.catalog.spark_catalog.warehouse": "{{S3_DATA_LOCATION}}",
        "spark.sql.catalog.spark_catalog.client.region": "{{REGION}}",
        "spark.sql.catalog.spark_catalog.type": "glue",
        "spark.sql.catalog.spark_catalog.glue.account-id": "{{ACCOUNT_ID}}",
        "spark.sql.catalog.spark_catalog.glue.lakeformation-enabled": "true",
        "spark.sql.catalog.dropDirectoryBeforeTable.enabled": "true" 
    }
}
```

------
#### [ For Delta Lake tables ]

```
%%configure -f
{
    "conf": {
        "spark.hadoop.fs.s3.credentialsResolverClass": "com.amazonaws.glue.accesscontrol.AWSLakeFormationCredentialResolver",
        "spark.hadoop.fs.s3.useDirectoryHeaderAsFolderObject": true,
        "spark.hadoop.fs.s3.folderObject.autoAction.disabled": true,
        "spark.sql.catalog.skipLocationValidationOnCreateTable.enabled": true,
        "spark.sql.catalog.createDirectoryAfterTable.enabled": true,
        "spark.sql.catalog.dropDirectoryBeforeTable.enabled": true
    }
}
```

------
#### [ For Hudi tables ]

```
%%configure -f
{
    "conf": {
        "spark.hadoop.fs.s3.credentialsResolverClass": "com.amazonaws.glue.accesscontrol.AWSLakeFormationCredentialResolver",
        "spark.hadoop.fs.s3.useDirectoryHeaderAsFolderObject": true,
        "spark.hadoop.fs.s3.folderObject.autoAction.disabled": true,
        "spark.sql.catalog.skipLocationValidationOnCreateTable.enabled": true,
        "spark.sql.catalog.createDirectoryAfterTable.enabled": true,
        "spark.sql.catalog.dropDirectoryBeforeTable.enabled": true,
        "spark.jars": "/usr/lib/hudi/hudi-spark-bundle.jar",
        "spark.sql.extensions": "org.apache.spark.sql.hudi.HoodieSparkSessionExtension",
        "spark.sql.catalog.spark_catalog": "org.apache.spark.sql.hudi.catalog.HoodieCatalog",
        "spark.serializer": "org.apache.spark.serializer.KryoSerializer"
    }
}
```

------

替换占位符：
+ `S3_DATA_LOCATION`：您的 S3 存储桶路径
+ `REGION`: AWS 区域（例如 us-east-1）
+ `ACCOUNT_ID`: 您的 AWS 账户 ID

**注意**  
必须在笔记本中执行任何 Spark 操作之前设置这些配置。

#### 支持的操作
<a name="lake-formation-unfiltered-supported-operations"></a>

这些操作将使用 AWS Lake Formation 凭据来访问表格数据。
+ CREATE TABLE
+ ALTER TABLE
+ INSERT INTO
+ INSERT OVERWRITE
+ UPDATE
+ MERGE INTO
+ DELETE FROM
+ ANALYZE TABLE
+ REPAIR TABLE
+ DROP TABLE
+ Spark Datasource 查询
+ Spark Datasource 写入

**注意**  
上面未列出的操作将继续使用 IAM 权限来访问表数据。

#### 注意事项
<a name="considerations"></a>
+ 如果使用未启用全表访问的作业创建 Hive 表，并且未插入任何记录，则后续从启用全表访问的作业进行的读取或写入操作都将失败。这是因为未启用全表访问的 EMR Spark 会将 `$folder$` 后缀添加到表文件夹名称。要解决此问题，您可以使用以下任一方法：
  + 从未启用 FTA 的作业在表中至少插入一行。
  + 配置未启用 FTA 的作业，从而确保不在 S3 文件夹名称中使用 `$folder$` 后缀。可以通过设置 Spark 配置 `spark.hadoop.fs.s3.useDirectoryHeaderAsFolderObject=true` 来实现此目的。
  + `s3://path/to/table/table_name`使用 S3 控制台或 S3 CLI 在表格位置创建 AWS S3 文件夹。 AWS 
+ 从 Amazon EMR 版本 7.8.0 开始，EMR 文件系统 (EMRFS) 支持全表访问，从 Amazon EMR 版本 7.10.0 开始，S3A 文件系统支持全表访问。
+ Hive、Iceberg、Delta 和 Hudi 表支持完整表访问权限。
+ **Hudi FTA Write Support 注意事项：**
  + Hudi FTA 写入要求在作业执行期间使用 HoodieCredentialedHadoopStorage 凭证自动售出。运行 Hudi 作业时设置以下配置：`hoodie.storage.class=org.apache.spark.sql.hudi.storage.HoodieCredentialedHadoopStorage`
  + 从 Amazon EMR 7.12 版本开始，Hudi 的全表访问权限 (FTA) 写入支持已推出。
  + Hudi FTA 写入支持目前仅适用于默认 Hudi 配置。自定义或非默认 Hudi 设置可能无法完全支持，并可能导致意外行为。
  + 在 FTA 写入模式下，目前不支持 Hudi Merge-On-Read (MOR) 表的群集。
+ 使用 Lake Formation 精细访问控制 (FGAC) 规则或 Glue Data Catalog 视图引用表的作业将会失败。要使用 FGAC 规则或 Glue Data Catalog 视图查询表，您必须使用 FGAC 模式。您可以按照 AWS 文档中概述的步骤启用 FGAC 模式：使用 [EMR Serverless 和 Lake Formation 进行精 AWS 细的访问](emr-serverless-lf-enable.html)控制。
+ 全表访问模式不支持 Spark Streaming。
+ 将 Spark DataFrame 写入 Lake Formation 表时，Hive 和 Iceberg 表仅支持 APPEND 模式：`df.write.mode("append").saveAsTable({{table_name}})`
+ 创建外部表需要 IAM 权限。
+ 由于 Lake Formation 会在 Spark 作业中临时缓存凭证，因此当前正在运行的 Spark 批处理作业或交互式会话可能无法反映权限更改。
+ 您必须使用用户定义的角色而不是服务相关角色：[Lake Formation 对角色的要求](https://docs.aws.amazon.com/lake-formation/latest/dg/registration-role.html)。

#### Hudi FTA Write Support——支持的操作
<a name="hudi-fta-supported-operations"></a>

下表显示了 Hudi Copy-On-Write (COW) 和 Merge-On-Read (MOR) 表在 “全表访问” 模式下支持的写入操作：


**Hudi FTA 支持的写入操作**  

| 表类型 | 操作 | SQL 写入命令 | Status | 
| --- | --- | --- | --- | 
| 牛 | INSERT | INSERT INTO TABLE | 支持 | 
| 牛 | INSERT | 插入表格-分区（静态、动态） | 支持 | 
| 牛 | INSERT | INSERT OVERWRITE | 支持 | 
| 牛 | INSERT | 插入覆盖-分区（静态、动态） | 支持 | 
| UPDATE | UPDATE | UPDATE TABLE | 支持 | 
| 牛 | UPDATE | 更新表-更改分区 | 不支持 | 
| DELETE | DELETE | DELETE FROM TABLE | 支持 | 
| ALTER | ALTER | 更改表-重命名为 | 不支持 | 
| 牛 | ALTER | 更改表格-设置 TBLPROPERTIES | 支持 | 
| 牛 | ALTER | 更改表格-未设置 TBLPROPERTIES | 支持 | 
| 牛 | ALTER | 更改表格-更改列 | 支持 | 
| 牛 | ALTER | 更改表格-添加列 | 支持 | 
| 牛 | ALTER | 更改表-添加分区 | 支持 | 
| 牛 | ALTER | 更改表-删除分区 | 支持 | 
| 牛 | ALTER | 更改表-恢复分区 | 支持 | 
| 牛 | ALTER | 修复表同步分区 | 支持 | 
| DROP | DROP | DROP TABLE | 支持 | 
| 牛 | DROP | 删除表-清除 | 支持 | 
| CREATE | CREATE | 创建表-托管 | 支持 | 
| 牛 | CREATE | 创建表-分区依据 | 支持 | 
| 牛 | CREATE | 如果不存在则创建表 | 支持 | 
| 牛 | CREATE | CREATE TABLE LIKE | 支持 | 
| 牛 | CREATE | CREATE TABLE AS SELECT | 支持 | 
| CREATE | CREATE | 使用位置创建表-外部表 | 不支持 | 
| 数据框（插入） | 数据框（插入） | saveAsTable. 覆盖 | 支持 | 
| 牛 | 数据框（插入） | saveAsTable.Append | 不支持 | 
| 牛 | 数据框（插入） | saveAsTable。忽略 | 支持 | 
| 牛 | 数据框（插入） | saveAsTable.ErrorIfExists | 支持 | 
| 牛 | 数据框（插入） | saveAsTable -外部表（路径） | 不支持 | 
| 牛 | 数据框（插入） | 保存（路径）-DF v1 | 不支持 | 
| 更多 | INSERT | INSERT INTO TABLE | 支持 | 
| 更多 | INSERT | 插入表格-分区（静态、动态） | 支持 | 
| 更多 | INSERT | INSERT OVERWRITE | 支持 | 
| 更多 | INSERT | 插入覆盖-分区（静态、动态） | 支持 | 
| UPDATE | UPDATE | UPDATE TABLE | 支持 | 
| 更多 | UPDATE | 更新表-更改分区 | 不支持 | 
| DELETE | DELETE | DELETE FROM TABLE | 支持 | 
| ALTER | ALTER | 更改表-重命名为 | 不支持 | 
| 更多 | ALTER | 更改表格-设置 TBLPROPERTIES | 支持 | 
| 更多 | ALTER | 更改表格-未设置 TBLPROPERTIES | 支持 | 
| 更多 | ALTER | 更改表格-更改列 | 支持 | 
| 更多 | ALTER | 更改表格-添加列 | 支持 | 
| 更多 | ALTER | 更改表-添加分区 | 支持 | 
| 更多 | ALTER | 更改表-删除分区 | 支持 | 
| 更多 | ALTER | 更改表-恢复分区 | 支持 | 
| 更多 | ALTER | 修复表同步分区 | 支持 | 
| DROP | DROP | DROP TABLE | 支持 | 
| 更多 | DROP | 删除表-清除 | 支持 | 
| CREATE | CREATE | 创建表-托管 | 支持 | 
| 更多 | CREATE | 创建表-分区依据 | 支持 | 
| 更多 | CREATE | 如果不存在则创建表 | 支持 | 
| 更多 | CREATE | CREATE TABLE LIKE | 支持 | 
| 更多 | CREATE | CREATE TABLE AS SELECT | 支持 | 
| CREATE | CREATE | 使用位置创建表-外部表 | 不支持 | 
| DATAFRAME（UPSERT） | DATAFRAME（UPSERT） | saveAsTable. 覆盖 | 支持 | 
| 更多 | DATAFRAME（UPSERT） | saveAsTable.Append | 不支持 | 
| 更多 | DATAFRAME（UPSERT） | saveAsTable。忽略 | 支持 | 
| 更多 | DATAFRAME（UPSERT） | saveAsTable.ErrorIfExists | 支持 | 
| 更多 | DATAFRAME（UPSERT） | saveAsTable -外部表（路径） | 不支持 | 
| 更多 | DATAFRAME（UPSERT） | 保存（路径）-DF v1 | 不支持 | 
| 数据框（删除） | 数据框（删除） | saveAsTable.Append | 不支持 | 
| 更多 | 数据框（删除） | saveAsTable -外部表（路径） | 不支持 | 
| 更多 | 数据框（删除） | 保存（路径）-DF v1 | 不支持 | 
| 数据框（批量插入） | 数据框（批量插入） | saveAsTable. 覆盖 | 支持 | 
| 更多 | 数据框（批量插入） | saveAsTable.Append | 不支持 | 
| 更多 | 数据框（批量插入） | saveAsTable。忽略 | 支持 | 
| 更多 | 数据框（批量插入） | saveAsTable.ErrorIfExists | 支持 | 
| 更多 | 数据框（批量插入） | saveAsTable -外部表（路径） | 不支持 | 
| 更多 | 数据框（批量插入） | 保存（路径）-DF v1 | 不支持 | 