기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.
데이터 스캔 양 감소
시작할 때 필요한 데이터만 로드하는 방법을 고려합니다. 각 데이터 소스에 대해 Spark 클러스터에 로드되는 데이터의 양을 줄임으로써 성능을 개선할 수 있습니다. 이 접근 방식이 적절한지 평가하려면 다음 지표를 사용합니다.
Spark UI 섹션에 설명된 대로 CloudWatch 지표에서 Amazon S3의 읽기 바이트 및 Spark UI에서 추가 세부 정보를 확인할 수 있습니다.
CloudWatch 지표
ETL 데이터 이동(바이트)에서 Amazon S3의 대략적인 읽기 크기를 확인할 수 있습니다. 이 지표는 이전 보고서 이후 모든 실행기가 Amazon S3에서 읽은 바이트 수를 보여줍니다. 이를 사용하여 Amazon S3의 ETL 데이터 이동을 모니터링할 수 있으며, 읽기와 외부 데이터 소스의 수집 속도를 비교할 수 있습니다.
예상보다 큰 S3 바이트 읽기 데이터 포인트가 관찰되는 경우 다음 솔루션을 고려합니다.
Spark UI
Spark UI AWS Glue 용의 스테이지 탭에서 입력 및 출력 크기를 볼 수 있습니다. 다음 예제의 경우 2단계에서는 47.4GiB 입력 및 47.7GiB 출력을 읽는 반면, 5단계에서는 61.2MiB 입력 및 56.6MiB 출력을 읽습니다.
AWS Glue 작업에서 Spark SQL 또는 DataFrame 접근 방식을 사용하면 SQL /D ataFrame 탭에 이러한 단계에 대한 더 많은 통계가 표시됩니다. 이 경우 2단계에서 읽은 파일 수: 430, 읽은 파일 크기: 47.4GiB, 출력 행 수: 160,796,570이 표시됩니다.
읽고 있는 데이터와 사용 중인 데이터 사이에서 크기에 상당한 차이가 있는 경우 다음 솔루션을 시도해 보세요.
Amazon S3
Amazon S3에서 읽을 때 작업에 로드되는 데이터의 양을 줄이려면 데이터 세트의 파일 크기, 압축, 파일 형식 및 파일 레이아웃(파티션) 을 고려하세요. AWS Glue Spark 작업은 원시 데이터의 ETL에 자주 사용되지만 효율적인 분산 처리를 위해서는 데이터 소스 형식의 기능을 검사해야 합니다.
-
파일 크기 - 입력 및 출력의 파일 크기를 중간 범위(예: 128MB) 이내로 유지하는 것이 좋습니다. 너무 작은 파일과 너무 큰 파일은 문제가 될 수 있습니다.
작은 파일이 많으면 다음과 같은 문제가 발생합니다.
-
많은 객체에 대한 요청(예:
List,Get또는Head)을 수행하는 데 필요한 오버헤드로 인한 Amazon S3에서 과도한 네트워크 I/O 로드(동일한 양의 데이터를 저장하는 몇 개의 객체와 비교한 경우). -
Spark 드라이버에서 과도한 I/O 및 처리 로드가 발생하여 많은 파티션과 태스크가 생성되고 과도한 병렬 처리가 발생합니다.
반면 파일 유형이 분할 가능하지 않고(예: gzip) 파일이 너무 큰 경우 단일 태스크가 전체 파일 읽기를 완료할 때까지 Spark 애플리케이션이 기다려야 합니다.
각 작은 파일에 대해 Apache Spark 태스크가 생성될 때 발생하는 과도한 병렬 처리를 줄이려면 DynamicFrames에 파일 그룹화를 사용합니다. 이 접근 방식은 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 리더는
GetObjectAPI에 특정 블록만 검색하는Range옵션을 제공하여 S3 객체에서 분할을 검색할 수 있습니다. 다음 다이어그램을 고려하여 실제로 어떻게 작동하는지 확인합니다.
압축된 데이터는 파일이 최적의 크기이거나 파일이 분할 가능한 한 애플리케이션의 속도를 크게 높일 수 있습니다. 데이터 크기가 작을수록 Amazon S3에서 스캔한 데이터와 Amazon S3에서 Spark 클러스터로의 네트워크 트래픽이 줄어듭니다. 반면 데이터를 압축 및 압축 해제하려면 더 많은 CPU가 필요합니다. 필요한 컴퓨팅 양은 압축 알고리즘의 압축 비율에 따라 조정됩니다. 분할 가능한 압축 형식을 선택할 경우 이 장단점을 고려합니다.
참고
gzip 파일은 일반적으로 분할할 수 없지만 개별 Parquet 블록을 gzip으로 압축할 수 있으며 이러한 블록은 병렬화할 수 있습니다.
-
-
파일 형식 - 열 형식을 사용합니다. Apache Parquet
및 Apache ORC 는 널리 사용되는 열 기반 데이터 형식입니다. Parquet 및 ORC는 열 기반 압축을 사용하고 데이터 유형에 따라 각 열을 인코딩 및 압축하여 데이터를 효율적으로 저장합니다. Parquet 인코딩에 대한 자세한 내용은 Parquet encoding definitions 를 참조하세요. 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 설명서의 접두사를 사용한 객체 구성을 참조하세요. AWS Glue 는
key=value형식의 Amazon S3 접두사에 키와 값을 저장하고 Amazon S3 경로를 통해 데이터를 분할할 수 있도록 지원합니다. 데이터를 분할하면 각 다운스트림 분석 애플리케이션이 스캔하는 데이터의 양을 제한하여 성능을 향상시키고 비용을 절감할 수 있습니다. 자세한 내용은 에서 ETL 출력을 위한 파티션 관리를 참조하세요 AWS Glue.분할은 테이블을 여러 부분으로 나누고 다음 예제와 같이 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'") -
데이터를에 쓸 때 날짜별로 파티셔 AWS Glue닝하려면 다음과 같이 열의 날짜 정보로 DataFrame의 DynamicFrame partitionKeys 또는 partitionBy()
를 설정합니다. -
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에서 읽을 때 제외를 설정합니다. 예를 들어 다음 코드는 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 파티션 인덱스를 사용하여 Data Catalog에서 파티션 메타데이터를 검색하는 데 걸리는 지연 시간을 줄이는 방법을 고려합니다. -
데이터베이스 및 JDBC
데이터베이스에서 정보를 검색할 때 데이터 스캔을 줄이려면 SQL 쿼리에서 where 조건자(또는 절)를 지정할 수 있습니다. SQL 인터페이스를 제공하지 않는 데이터베이스는 쿼리 또는 필터링을 위한 자체 메커니즘을 제공합니다.
Java Database Connectivity(JDBC) 연결을 사용하는 경우 다음 파라미터에 대한 where 절과 함께 선택 쿼리를 제공합니다.
-
DynamicFrame의 경우 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의 경우 쿼리
옵션을 사용합니다. 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 커넥터에서 푸시다운 지원을 활용하려면 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"} ) -
다른 데이터베이스의 경우 해당 데이터베이스에 대한 설명서를 참조하세요.