

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 在 AWS Glue 中管理適用於 ETL 輸出的分割區
<a name="aws-glue-programming-etl-partitions"></a>

分割區是組織資料集的一項重要技術，您可以有效率地對它們進行查詢。分割區會根據一或多個欄位的不同值來組織階層目錄結構組織中的資料。

例如，您可以決定在 Amazon Simple Storage Service (Amazon S3) 中以日期來分割應用程式日誌，並依年、月和天來劃分。與單一天資料對應的檔案會放在字首 (例如 `s3://my_bucket/logs/year=2018/month=01/day=23/`)。系統 (如 Amazon Athena、Amazon Redshift Spectrum 與現在的 AWS Glue) 可以使用這些分割區來依分割區值篩選資料，而不必從 Amazon S3 讀取所有基礎資料。

爬蟲程式不僅推斷檔案類型和結構描述，還會在填入 Glue Data Catalog AWS 時自動識別資料集的分割區結構。產生的分割區資料行可用於在 AWS Glue ETL 任務中查詢，或查詢 Amazon Athena 之類的引擎。

在您編目資料表後，您可以查看爬蟲程式建立的分割區。在 AWS Glue 主控台，於左側導覽窗格選擇 **Tables** (資料表)。選擇爬蟲程式建立的資料表，然後選擇 **View Partitions (檢視分割區)**。

對於樣式為 `key=val` 的 Apache Hive 樣式分割區路徑，爬蟲程式會自動使用金鑰名稱填入欄位名稱。否則，它會使用預設名稱，如 `partition_0`、`partition_1`，以此類推。您可以在主控台上變更預設名稱。請導覽至資料表執行此操作。在**索引**索引標籤下檢查索引是否存在。如果存在，您需要刪除索引才能繼續 (之後可以使用新資料欄名稱重新建立索引)。然後，選擇**編輯結構描述**，並在該處修改分割區資料欄的名稱。

在您的 ETL 指令碼中，所以您可以在分割區欄上篩選。因為分割區資訊儲存在 Data Catalog 中，請使用 `from_catalog` API 呼叫，以將分割區欄包含在 `DynamicFrame` 中。例如，使用 `create_dynamic_frame.from_catalog` 代替 `create_dynamic_frame.from_options`。

磁碟分割是減少資料掃描的最佳化技術。如需有關識別此技術何時適當之程序的詳細資訊，請參閱 AWS 《 方案指南》中的*效能調校 AWS Glue for Apache Spark 任務最佳實務*指南》中的[減少資料掃描量](https://docs.aws.amazon.com/prescriptive-guidance/latest/tuning-aws-glue-for-apache-spark/reduce-data-scan.html)。

## 使用 pushdown 述詞預先篩選
<a name="aws-glue-programming-etl-partitions-pushdowns"></a>

在許多情況下，您可以使用 pushdown 述詞來在分割區上篩選，而無需列出和讀取資料集中的所有檔案。您不須在 DynamicFrame 中讀取整個資料集，然後才進行篩選，您可以直接在 Data Catalog 中分割區中繼資料上套用篩選。然後，您只需要列出與讀取在 DynamicFrame 中實際需要的項目。

例如，您可以在 Python 中寫入下列各項。

```
glue_context.create_dynamic_frame.from_catalog(
    database = "my_S3_data_set",
    table_name = "catalog_data_table",
    push_down_predicate = my_partition_predicate)
```

此會建立在 Data Catalog 中載入並滿足述詞表達式的 DynamicFrame。根據您載入的資料子集大小，這可以節省大量的處理時間。

述詞表達式可以是 Spark SQL 支援的任何布林表達式。您可以在 Spark SQL 查詢中放入 `WHERE` 子句的任何項目。例如，述詞表達式 `pushDownPredicate = "(year=='2017' and month=='04')"` 僅讀取 Data Catalog 中 `year`​ 等於 2017 且 `month` 等於 04 的分割區​。如需更多詳細資訊，請參閱「[Apache Spark SQL 文件](https://spark.apache.org/docs/2.1.1/sql-programming-guide.html)」，特別是「[Scala SQL 函數參考](https://spark.apache.org/docs/2.1.1/api/scala/index.html#org.apache.spark.sql.functions$)」。

## 使用目錄分割述詞的伺服器端篩選
<a name="aws-glue-programming-etl-partitions-cat-predicates"></a>

`push_down_predicate` 選項會在列出目錄中的所有分割區之後，以及列出 Amazon S3 中的這些分割區的檔案之前套用。如果您有資料表的很多分割區，目錄分割區列表仍會產生額外的時間負荷。若要解決此額外負荷，您可以使用伺服器端分割區剔除搭配 Glue Data Catalog AWS 中使用[分割區索引](https://docs.aws.amazon.com/glue/latest/dg/partition-indexes.html)`catalogPartitionPredicate`的選項。當您在一個資料表中有數百萬個分割區時，這會使分割區篩選速度更快。如果您的 `catalogPartitionPredicate` 需要目錄分割區索引尚未支援的述詞語法，您可以同時在 `additional_options` 中使用 `push_down_predicate` 和 `catalogPartitionPredicate`。

Python：

```
dynamic_frame = glueContext.create_dynamic_frame.from_catalog(
    database=dbname, 
    table_name=tablename,
    transformation_ctx="datasource0",
    push_down_predicate="day>=10 and customer_id like '10%'",
    additional_options={"catalogPartitionPredicate":"year='2021' and month='06'"}
)
```

Scala：

```
val dynamicFrame = glueContext.getCatalogSource(
    database = dbname,
    tableName = tablename, 
    transformationContext = "datasource0",
    pushDownPredicate="day>=10 and customer_id like '10%'",
    additionalOptions = JsonOptions("""{
        "catalogPartitionPredicate": "year='2021' and month='06'"}""")
    ).getDynamicFrame()
```

**注意**  
`push_down_predicate` 和 `catalogPartitionPredicate` 使用不同的語法。前者使用 Spark SQL 標準語法，後者使用 JSQL 剖析器。

## 寫入分割區
<a name="aws-glue-programming-etl-partitions-writing"></a>

在預設情況下，在對 DynamicFrame 寫入時，不會對其進行分割。會在指定輸出路徑的最高層級寫入所有輸出檔。先前，將 DynamicFrame 寫到分割區的唯一方法是將它轉換為 Spark SQL DataFrame 再進行寫入。

DynamicFrames 現支援使用一系列的金鑰來進行原生分割，在您建立目的地時使用 `partitionKeys` 選項。例如，以下 Python 程式碼會將資料集寫出至格式為 Parquet 的 Amazon S3 中，以類型欄位寫入分割的目錄中。您可以在此使用其他系統 (例如 Amazon Athena) 處理這些分割區。

```
glue_context.write_dynamic_frame.from_options(
    frame = projectedEvents,
    connection_type = "s3",    
    connection_options = {"path": "$outpath", "partitionKeys": ["type"]},
    format = "parquet")
```