

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

# 減少資料掃描量
<a name="reduce-data-scan"></a>

若要開始，請考慮僅載入您需要的資料。您只需減少每個資料來源載入 Spark 叢集的資料量，即可改善效能。若要評估此方法是否適當，請使用下列指標。

您可以在 [CloudWatch 指標](https://docs.aws.amazon.com/glue/latest/dg/monitoring-awsglue-with-cloudwatch-metrics.html)中檢查來自 Amazon S3 的讀取位元組，以及在 Spark UI 中檢查更多詳細資訊，如 [Spark UI](#scan-spark) 一節所述。

## CloudWatch 指標
<a name="scan-metrics"></a>

您可以在 [ETL 資料移動 （位元組）](https://docs.aws.amazon.com/glue/latest/dg/monitoring-awsglue-with-cloudwatch-metrics.html) 中查看 Amazon S3 的大約讀取大小。此指標顯示自上次報告以來，所有執行器從 Amazon S3 讀取的位元組數。您可以使用它來監控來自 Amazon S3 的 ETL 資料移動，而且您可以將讀取與來自外部資料來源的擷取速率進行比較。



![\[ETL 資料移動 （位元組） 圖形指標索引標籤，顯示寫入的 S3 位元組和讀取的 S3 位元組。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/tuning-aws-glue-for-apache-spark/images/etl-data-movements.png)


如果您觀察到比預期更大的 **S3 位元組讀取**資料點，請考慮下列解決方案。

## Spark 使用者介面
<a name="scan-spark"></a>

在 AWS Glue 適用於 Spark UI 的**階段**索引標籤上，您可以看到**輸入**和**輸出**大小。在下列範例中，階段 2 讀取 47.4 GiB 輸入和 47.7 GiB 輸出，而階段 5 讀取 61.2 MiB 輸入和 56.6 MiB 輸出。



![\[""\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/tuning-aws-glue-for-apache-spark/images/stages-for-all-jobs-2.png)


當您在 AWS Glue 任務中使用 Spark SQL 或 DataFrame 方法時，**SQL /D ataFrame **索引標籤會顯示這些階段的更多統計資料。在此情況下，階段 2** 會顯示檔案讀取數量：430**，**檔案讀取大小：47.4 GiB**，**輸出資料列數量：160，796，570**。



![\[""\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/tuning-aws-glue-for-apache-spark/images/details-query-0.png)


如果您發現您正在讀取的資料與您正在使用的資料之間存在很大的差異，請嘗試以下解決方案。

### Amazon S3
<a name="amazon-s3"></a>

若要減少從 Amazon S3 讀取時載入任務的資料量，請考慮資料集** **的檔案大小、壓縮、檔案格式和檔案配置 （分割區）。 AWS Glue 對於 Spark 任務通常用於原始資料的 ETL，但為了有效率的分散式處理，您需要檢查資料來源格式的功能。
+ **檔案大小** – 我們建議將輸入和輸出的檔案大小保持在中等範圍內 （例如 128 MB)。太小的檔案和太大的檔案可能會導致問題。

  大量小型檔案會導致下列問題：
  + Amazon S3 上的大量網路 I/O 負載，因為對許多物件 （相較於存放相同資料數量的幾個物件`Head`) 提出請求 （例如 `List``Get`、 或 ) 所需的額外負荷。
  + Spark 驅動程式上的大量 I/O 和處理負載，這會產生許多分割區和任務，並導致過度平行處理。

  另一方面，如果您的檔案類型無法分割 （例如 gzip) 且檔案太大，Spark 應用程式必須等到單一任務完成讀取整個檔案。

  若要減少為每個小型檔案建立 Apache Spark 任務時產生的過度平行處理，請使用 [ DynamicFrames 的檔案分組](https://docs.aws.amazon.com/glue/latest/dg/grouping-input-files.html)。此方法可降低 Spark 驅動程式發生 OOM 例外狀況的機率。若要設定檔案分組，請設定 `groupFiles`和 `groupSize` 參數。下列程式碼範例使用 ETL 指令碼中的 AWS Glue DynamicFrame API 搭配這些參數。

  ```
  dyf = glueContext.create_dynamic_frame_from_options("s3",
      {'paths': ["s3://input-s3-path/"],
      'recurse':True,
      'groupFiles': 'inPartition',
      'groupSize': '1048576'},
      format="json")
  ```
+ **壓縮** – 如果您的 S3 物件位於數百 MB 中，請考慮壓縮它們。有各種壓縮格式，可廣泛分類為兩種類型：
  + gzip 等*無法分割*** **的壓縮格式需要一個工作者解壓縮整個檔案。
  + *可分割*壓縮格式，例如 bzip2 或 LZO （索引），允許檔案的部分解壓縮，可平行處理。

  對於 Spark （和其他常見的分散式處理引擎），您可以將來源資料檔案分割為引擎可以平行處理的區塊。這些單位通常稱為*分割*。資料為可分割格式之後，最佳化的 AWS Glue 讀取器可以透過提供 `GetObject` API `Range`選項，僅擷取特定區塊，從 S3 物件擷取分割。請考慮下圖，了解這在實務上如何運作。

    
![\[三個 AWS Glue 工作者中的每一個都會連接到 Amazon S3 上的檔案分割。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/tuning-aws-glue-for-apache-spark/images/reduce-data-scan.png)

  只要檔案為最佳大小或檔案可分割，壓縮的資料就能大幅加速您的應用程式。較小的資料大小可減少從 Amazon S3 掃描的資料，以及從 Amazon S3 到 Spark 叢集的網路流量。另一方面，壓縮和解壓縮資料需要更多 CPU。所需的運算量會隨著壓縮演算法的壓縮比率而擴展。選擇可分割壓縮格式時，請考慮此取捨。
**注意**  
雖然 gzip 檔案通常無法分割，但您可以使用 gzip 壓縮個別 parquet 區塊，而且這些區塊可以平行化。
+ **檔案格式** – 使用單欄格式。[Apache Parquet](https://parquet.apache.org/) 和 [Apache ORC](https://orc.apache.org/) 是熱門的單欄式資料格式。Parquet 和 ORC 會根據資料類型採用資料欄型壓縮、編碼和壓縮每個資料欄，以有效率地存放資料。如需 Parquet 編碼的詳細資訊，請參閱 [Parquet 編碼定義](https://github.com/apache/parquet-format/blob/master/Encodings.md)。Parquet 檔案也可以分割。

  單欄格式依資料欄分組值，並將其存放在區塊中。使用單欄式格式時，您可以略過對應於您不打算使用之資料欄的資料區塊。Spark 應用程式只能擷取您需要的資料欄。一般而言，更好的壓縮率或略過資料區塊意味著從 Amazon S3 讀取較少的位元組，進而獲得更好的效能。這兩種格式也支援下列下推方法來減少 I/O：
  + **投影下推** – 投影下推是一種僅擷取應用程式中指定資料欄的技術。您可以在 Spark 應用程式中指定資料欄，如下列範例所示：
    + DataFrame 範例： `df.select("star_rating")`
    + Spark SQL 範例： `spark.sql("select start_rating from <table>")`
  + **述詞下推** – 述詞下推是一種有效處理 `WHERE` 和 `GROUP BY`子句的技術。這兩種格式都有代表資料欄值的資料區塊。每個區塊都會保留區塊的統計資料，例如最大值和最小值。Spark 可以使用這些統計資料，根據應用程式中使用的篩選條件值來判斷是否應讀取或略過區塊。若要使用此功能，請在條件中新增更多篩選條件，如下列範例所示：
    + DataFrame 範例： `df.select("star_rating").filter("star_rating < 2")`
    + Spark SQL 範例： `spark.sql("select * from <table> where star_rating < 2")`
+ **檔案配置** – 根據資料的使用方式，將 S3 資料儲存到不同路徑中的物件，您可以有效率地擷取相關資料。如需詳細資訊，請參閱《Amazon S3 文件》中的[使用字首整理物件](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-prefixes.html)。 AWS Glue 支援以 格式將金鑰和值儲存到 Amazon S3 字首`key=value`，並依 Amazon S3 路徑分割您的資料。透過分割資料，您可以限制每個下游分析應用程式掃描的資料量，從而改善效能並降低成本。如需詳細資訊，請參閱[管理 中 ETL 輸出的分割區 AWS Glue](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl-partitions.html#aws-glue-programming-etl-partitions-pushdowns.)。

  分割會將資料表分成不同的部分，並根據*年*、*月*和*日*等資料欄值，將相關資料保留在分組檔案中，如下列範例所示。

  ```
  # Partitioning by /YYYY/MM/DD
  s3://<YourBucket>/year=2023/month=03/day=31/0000.gz
  s3://<YourBucket>/year=2023/month=03/day=01/0000.gz
  s3://<YourBucket>/year=2023/month=03/day=02/0000.gz
  s3://<YourBucket>/year=2023/month=03/day=03/0000.gz
  ...
  ```

  您可以使用 中的資料表來建立資料集模型，藉此定義資料集的分割區 AWS Glue Data Catalog。然後，您可以使用*分割區剔除*來限制資料掃描的數量，如下所示：
  + For AWS Glue DynamicFrame，設定 `push_down_predicate`（或 `catalogPartitionPredicate`)。

    ```
    dyf = Glue_context.create_dynamic_frame.from_catalog(
        database=src_database_name,
        table_name=src_table_name,
        push_down_predicate = "year='2023' and month ='03'",
    )
    ```
  + 針對 Spark DataFrame，設定固定路徑以刪除分割區。

    ```
    df = spark.read.format("json").load("s3://<YourBucket>/year=2023/month=03/*/*.gz")
    ```
  + 對於 Spark SQL，您可以設定 where 子句從 Data Catalog 刪除分割區。

    ```
    df = spark.sql("SELECT * FROM <Table> WHERE year= '2023' and month = '03'")
    ```
  + 若要在使用 寫入資料時依日期分割 AWS Glue，請在 DataFrame 中的 DynamicFrame 或 [partitionBy()](https://spark.apache.org/docs/latest/api/python/reference/pyspark.sql/api/pyspark.sql.DataFrameWriter.partitionBy.html) 中設定 [partitionKeys](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl-partitions.html#aws-glue-programming-etl-partitions-writing)，資料欄中的日期資訊如下所示。
    + DynamicFrame

      ```
      glue_context.write_dynamic_frame_from_options(
          frame= dyf, connection_type='s3',format='parquet'
          connection_options= {
              'partitionKeys': ["year", "month", "day"],
              'path': 's3://<YourBucket>/<Prefix>/'
          }
      )
      ```
    + DataFrame

      ```
      df.write.mode('append')\
          .partitionBy('year','month','day')\
          .parquet('s3://<YourBucket>/<Prefix>/')
      ```

    這可以改善輸出資料消費者的效能。

    如果您無法存取 來修改建立輸入資料集的管道，則分割不是一個選項。反之，您可以使用 glob 模式來排除不需要的 S3 路徑。在 DynamicFrame 中讀取時設定[排除](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl-connect-s3-home.html#aws-glue-programming-etl-connect-s3)。例如，下列程式碼排除 2023 年的第 01 到 09 個月天數。

    ```
    dyf = glueContext.create_dynamic_frame.from_catalog(
        database=db, 
        table_name=table,
        additional_options = { "exclusions":"[\"**year=2023/month=0[1-9]/**\"]" },
        transformation_ctx='dyf' 
    )
    ```

    您也可以在 Data Catalog 的資料表屬性中設定排除：
    + 索引鍵：`exclusions`
    + 值：`["**year=2023/month=0[1-9]/**"]`
+ **太多 Amazon S3 分割區** – 避免在包含各種值的資料欄上分割 Amazon S3 資料，例如具有數千個值的 ID 資料欄。這可以大幅增加儲存貯體中的分割區數量，因為可能的分割區數量是您已分割的所有欄位的乘積。太多分割區可能會導致下列情況：
  + 從 Data Catalog 擷取分割區中繼資料的延遲增加
  + 需要更多 Amazon S3 API 請求的小型檔案數量增加 (`List`、 `Get`和 `Head`)

  例如，當您在 `partitionBy`或 中設定日期類型時`partitionKeys`， 等日期層級分割`yyyy/mm/dd`適用於許多使用案例。不過， `yyyy/mm/dd/<ID>`可能會產生太多分割區，進而對整體效能產生負面影響。

  另一方面，一些使用案例，例如即時處理應用程式，需要許多分割區，例如 `yyyy/mm/dd/hh`。如果您的使用案例需要大量分割區，請考慮使用[AWS Glue 分割區索引](https://docs.aws.amazon.com/glue/latest/dg/partition-indexes.html)來減少從 Data Catalog 擷取分割區中繼資料的延遲。

### 資料庫和 JDBC
<a name="jdbc"></a>

若要在從資料庫擷取資訊時減少資料掃描，您可以在 SQL 查詢中指定`where`述詞 （或 子句）。不提供 SQL 界面的資料庫將提供自己的查詢或篩選機制。

使用 Java Database Connectivity (JDBC) 連線時，請使用 `where`子句為下列參數提供選取查詢：
+ 對於 DynamicFrame，請使用 [sampleQuery](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl-connect-jdbc-home.html#aws-glue-programming-etl-jdbc-samplequery) 選項。使用 時`create_dynamic_frame.from_catalog`，請設定引`additional_options`數，如下所示。

  ```
  query = "SELECT * FROM <TableName> where id = 'XX' AND"
  datasource0 = glueContext.create_dynamic_frame.from_catalog(
    database = db,
    table_name = table,
    additional_options={
        "sampleQuery": query,
        "hashexpression": key,
        "hashpartitions": 10,
        "enablePartitioningForSampleQuery": True
      },
    transformation_ctx = "datasource0"
  )
  ```

  當 時`using create_dynamic_frame.from_options`，設定引`connection_options`數，如下所示。

  ```
  query = "SELECT * FROM <TableName> where id = 'XX' AND"
  datasource0 = glueContext.create_dynamic_frame.from_options(
      connection_type = connection, 
      connection_options={
          "url": url,
          "user": user,
          "password": password,
          "dbtable": table,
          "sampleQuery": query,
          "hashexpression": key,
          "hashpartitions": 10,
          "enablePartitioningForSampleQuery": True
      }
  )
  ```
+ 對於 DataFrame，請使用 [查詢](https://spark.apache.org/docs/latest/sql-data-sources-jdbc.html)選項。

  ```
  query = "SELECT * FROM <TableName> where id = 'XX'"
  jdbcDF = spark.read \
  .format('jdbc') \
  .option('url', url) \
  .option('user', user) \
  .option('password', pwd) \
  .option('query', query) \
  .load()
  ```
+ 對於 Amazon Redshift，請使用 AWS Glue 4.0 或更新版本來利用 [Amazon Redshift Spark 連接器](https://docs.aws.amazon.com/redshift/latest/mgmt/spark-redshift-connector.html)中的下推支援。

  ```
  dyf = glueContext.create_dynamic_frame.from_catalog(
      database = "redshift-dc-database-name", 
      table_name = "redshift-table-name", 
      redshift_tmp_dir = args["temp-s3-dir"], 
      additional_options = {"aws_iam_role": "arn:aws:iam::role-account-id:role/rs-role-name"}
  )
  ```
+ 如需其他資料庫，請參閱該資料庫的文件。

### AWS Glue 選項
<a name="options"></a>
+ 若要避免對所有連續任務執行進行完整掃描，並僅處理上次任務執行期間不存在的資料，請啟用[任務書籤](https://docs.aws.amazon.com/glue/latest/dg/monitor-continuations.html)。
+ 若要限制要處理的輸入資料數量，請使用任務書籤啟用[週框執行](https://docs.aws.amazon.com/glue/latest/dg/bounded-execution.html)。這有助於減少每個任務執行的掃描資料量。