

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# 데이터 스캔 양 감소
<a name="reduce-data-scan"></a>

시작할 때 필요한 데이터만 로드하는 방법을 고려합니다. 각 데이터 소스에 대해 Spark 클러스터에 로드되는 데이터의 양을 줄임으로써 성능을 개선할 수 있습니다. 이 접근 방식이 적절한지 평가하려면 다음 지표를 사용합니다.

[Spark UI](#scan-spark) 섹션에 설명된 대로 [CloudWatch 지표](https://docs.aws.amazon.com/glue/latest/dg/monitoring-awsglue-with-cloudwatch-metrics.html)에서 Amazon S3의 읽기 바이트 및 Spark UI에서 추가 세부 정보를 확인할 수 있습니다.

## 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 데이터 이동을 모니터링할 수 있으며, 읽기와 외부 데이터 소스의 수집 속도를 비교할 수 있습니다.



![S3 바이트 쓰기 및 S3 바이트 읽기를 보여주는 ETL 데이터 이동(바이트) 그래프 지표 탭.](http://docs.aws.amazon.com/ko_kr/prescriptive-guidance/latest/tuning-aws-glue-for-apache-spark/images/etl-data-movements.png)


예상보다 큰 **S3 바이트 읽기** 데이터 포인트가 관찰되는 경우 다음 솔루션을 고려합니다.

## Spark UI
<a name="scan-spark"></a>

Spark UI AWS Glue 용의 **스테이지** 탭에서 **입력** 및** 출력 **크기를 볼 수 있습니다. 다음 예제의 경우 2단계에서는 47.4GiB 입력 및 47.7GiB 출력을 읽는 반면, 5단계에서는 61.2MiB 입력 및 56.6MiB 출력을 읽습니다.



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


 AWS Glue 작업에서 Spark SQL 또는 DataFrame 접근 방식을 사용하면 **SQL / DataFrame** 탭에 이러한 단계에 대한 더 많은 통계가 표시됩니다. 이 경우 2단계에서 **읽은 파일 수: 430**, **읽은 파일 크기: 47.4GiB**, **출력 행 수: 160,796,570**이 표시됩니다.



![""](http://docs.aws.amazon.com/ko_kr/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에 자주 사용되지만 효율적인 분산 처리를 위해서는 데이터 소스 형식의 기능을 검사해야 합니다.
+ **파일 크기** - 입력 및 출력의 파일 크기를 중간 범위(예: 128MB) 이내로 유지하는 것이 좋습니다. 너무 작은 파일과 너무 큰 파일은 문제가 될 수 있습니다.

  작은 파일이 많으면 다음과 같은 문제가 발생합니다.
  + 많은 객체에 대한 요청(예: `List`, `Get` 또는 `Head`)을 수행하는 데 필요한 오버헤드로 인한 Amazon S3에서 과도한 네트워크 I/O 로드(동일한 양의 데이터를 저장하는 몇 개의 객체와 비교한 경우).
  + 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 객체가 수백 메가바이트인 경우 압축하는 것이 좋습니다. 다양한 압축 형식이 있으며, 크게 두 가지 유형으로 분류할 수 있습니다.
  + gzip과 같은 *분할할 수 없는*** **압축 형식을 사용하려면 한 작업자가 전체 파일의 압축을 풀어야 합니다.
  + bzip2 또는 LZO(인덱스)와 같은 *분할 가능한* 압축 형식은 병렬화할 수 있는 파일의 부분 압축 해제를 허용합니다.

  Spark 및 기타 일반적인 분산 처리 엔진의 경우 소스 데이터 파일을 엔진이 병렬로 처리할 수 있는 청크로 분할합니다. 이러한 단위를 종종 *분할*이라고 합니다. 데이터가 분할 가능한 형식이면 최적화된 AWS Glue 리더는 `GetObject` API에 특정 블록만 검색하는 `Range` 옵션을 제공하여 S3 객체에서 분할을 검색할 수 있습니다. 다음 다이어그램을 고려하여 실제로 어떻게 작동하는지 확인합니다.

    
![세 AWS Glue 작업자는 각각 Amazon S3의 파일 분할에 연결됩니다.](http://docs.aws.amazon.com/ko_kr/prescriptive-guidance/latest/tuning-aws-glue-for-apache-spark/images/reduce-data-scan.png)

  압축된 데이터는 파일이 최적의 크기이거나 파일이 분할 가능한 한 애플리케이션의 속도를 크게 높일 수 있습니다. 데이터 크기가 작을수록 Amazon S3에서 스캔한 데이터와 Amazon S3에서 Spark 클러스터로의 네트워크 트래픽이 줄어듭니다. 반면 데이터를 압축 및 압축 해제하려면 더 많은 CPU가 필요합니다. 필요한 컴퓨팅 양은 압축 알고리즘의 압축 비율에 따라 조정됩니다. 분할 가능한 압축 형식을 선택할 경우 이 장단점을 고려합니다.
**참고**  
gzip 파일은 일반적으로 분할할 수 없지만 개별 Parquet 블록을 gzip으로 압축할 수 있으며 이러한 블록은 병렬화할 수 있습니다.
+ **파일 형식** - 열 형식을 사용합니다. [Apache Parquet](https://parquet.apache.org/) 및 [Apache ORC](https://orc.apache.org/)는 널리 사용되는 열 기반 데이터 형식입니다. Parquet 및 ORC는 열 기반 압축을 사용하고 데이터 유형에 따라 각 열을 인코딩 및 압축하여 데이터를 효율적으로 저장합니다. Parquet 인코딩에 대한 자세한 내용은 [Parquet encoding definitions](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 는 `key=value` 형식의 Amazon S3 접두사에 키와 값을 저장하고 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.).

  분할은 테이블을 여러 부분으로 나누고 다음 예제와 같이 *year*, *month*, *day* 등의 열 값을 기반으로 관련 데이터를 그룹화된 파일에 보관합니다.

  ```
  # 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 절을 설정할 수 있습니다.

    ```
    df = spark.sql("SELECT * FROM <Table> WHERE year= '2023' and month = '03'")
    ```
  + 데이터를 쓸 때 날짜별로 분할하려면 다음과 같이 열의 날짜 정보로 DataFrame의 DynamicFrame 또는 [partitionBy()에서 partitionKeys](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)를 AWS Glue설정합니다.
    + 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년 1월\~9월의 일수를 제외합니다.

    ```
    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 파티션이 너무 많음** - 값이 수천 개인 ID 열과 같이 광범위한 값을 포함하는 열에서 Amazon S3 데이터를 분할하지 마세요. 그러면 가능한 파티션 수는 파티션을 분할한 모든 필드의 곱이므로 버킷의 파티션 수가 크게 증가할 수 있습니다. 파티션이 너무 많으면 다음과 같은 상황이 나타날 수 있습니다.
  + 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의 경우 Amazon [Redshift Spark 커넥터](https://docs.aws.amazon.com/redshift/latest/mgmt/spark-redshift-connector.html)에서 푸시다운 지원을 활용하려면 AWS Glue 4.0 이상을 사용합니다.

  ```
  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)을 활성화합니다. 이렇게 하면 각 작업 실행에 대해 스캔한 데이터의 양을 줄일 수 있습니다.