이 문서는 Databricks, Apache Spark 및 Delta Lake의 필수 모범 사례와 최적화 기술을 한곳에 모아 제공합니다. 모든 데이터 엔지니어와 데이터 아키텍트가 최적화되고 비용 효율적인 데이터 파이프라인을 설계하고 개발할 때 참고할 수 있는 가이드입니다. 비용은 사후 고려사항이 아닌 프로젝트 초기 단계부터 가장 중요한 비기능적 요구사항 중 하나로 다루어져야 합니다.
저자:
Himanshu Arora, Senior Resident Solutions Architect
Prashanth Babu Velanati Venkata, Lead Product Specialist
Delta Lake는 데이터 레이크에 신뢰성, 보안 및 성능을 제공하는 오픈 포맷 스토리지 레이어입니다. Spark, PrestoDB, Flink, Trino, Hive와 같은 컴퓨팅 엔진과 Python, SQL, Scala, Java, Rust, Ruby용 API를 사용하여 레이크하우스 아키텍처를 구축할 수 있습니다. Delta는 Parquet, Avro, ORC 등 다른 오픈 포맷보다 많은 이점을 제공합니다:
따라서 모든 이점을 활용하기 위해 Delta를 기본 데이터 레이크 스토리지 포맷으로 사용하는 것이 좋습니다. Databricks DBR 8.x 이상에서는 Delta Lake가 기본 포맷입니다.
Delta 테이블 내부에는 데이터를 저장하는 Parquet 파일이 있습니다. 또한 Parquet 파일 옆에 Delta 트랜잭션 로그를 저장하는 __delta_log_
서브디렉토리가 있습니다. 이러한 Parquet 파일의 크기는 쿼리 성능에 매우 중요합니다.
작은 파일 문제는 빅데이터 세계에서 잘 알려진 문제입니다. 테이블에 작은 파일이 너무 많으면 이러한 작은 파일을 열고 닫는 데 소요되는 시간으로 인해 읽기 지연 시간이 발생합니다.
이 문제를 방지하려면 파일 크기를 16MB에서 1GB 사이로 유지하는 것이 좋습니다. 이는 워크로드와 특정 사용 사례에 따라 사례별로 구성할 수 있습니다.
OPTIMIZE는 파일을 압축하여 최대 1GB의 파일 크기를 얻으며, 이는 구성 가능합니다. 이 명령은 기본적으로 구성한 크기(또는 구성하지 않은 경우 기본 1GB)로 파일 크기를 조정하려고 시도합니다. OPTIMIZE 명령을 ZORDER와 결합할 수도 있는데, 이는 선택한 열을 기준으로 데이터를 물리적으로 정렬하거나 함께 배치합니다.
모범 사례:
customer_id
)을 선택하세요.Auto optimize는 이름에서 알 수 있듯이 Delta 테이블에 쓸 때 작은 파일을 자동으로 압축하며, 기본적으로 128MB의 파일 크기를 달성하려고 시도합니다. 다음 두 가지 기능이 포함됩니다:
Optimize Write: 실제 데이터를 기반으로 Apache Spark 파티션 크기를 동적으로 최적화하고 각 테이블 파티션에 대해 128MB 파일을 작성하려고 시도합니다.
Auto Compact: Spark 작업이 완료된 후 새 작업을 시작하여 파일을 추가로 압축하여 128MB 파일 크기를 달성할 수 있는지 확인합니다.
파티셔닝을 사용하면 파티션 열을 필터로 제공하거나 파티션 열을 조인하거나 파티션 열을 집계할 때 쿼리 속도를 높일 수 있습니다. 스캔 시 불필요한 데이터 파티션(하위 폴더)을 건너뛰는 데 도움이 됩니다.
모범 사례:
자동 최적화(128MB) 또는 최적화(1GB)에서 타겟팅하는 기본 파일 크기가 적합하지 않은 경우 요구 사항에 따라 미세 조정할 수 있습니다. delta.targetFileSize
테이블 속성을 사용하여 대상 파일 크기를 설정할 수 있습니다.
또한 delta.tuneFileSizesForRewrites
테이블 속성을 켜서 이 작업을 Databricks에 위임할 수도 있습니다. 이 속성이 true로 설정되면 Databricks는 워크로드를 기반으로 파일 크기를 자동으로 조정합니다.
데이터 셔플은 조인, 집계, 윈도우 연산 등과 같은 넓은 변환(wide transformations)의 결과로 발생합니다. 네트워크를 통해 작업자 노드 간에 데이터를 전송하기 때문에 비용이 많이 드는 프로세스입니다. 셔플을 완전히 제거하거나 셔플의 효율성과 속도를 향상시키기 위해 몇 가지 최적화 접근 방식을 사용할 수 있습니다.
데이터 셔플링을 완전히 피하기 위해 조인되는 두 테이블 또는 DataFrame 중 하나(더 작은 것)를 브로드캐스트합니다. 테이블은 드라이버에 의해 브로드캐스트되어 모든 작업자 노드에 복사됩니다.
조인을 실행할 때 Spark는 자동으로 10MB 미만의 테이블을 브로드캐스트합니다. 그러나 더 큰 테이블을 브로드캐스트하도록 이 임계값을 조정할 수 있습니다:
spark.conf.set("spark.sql.autoBroadcastJoinThreshold", 104857600) // 100MB로 설정
쿼리에 있는 일부 테이블이 작은 테이블임을 알고 있는 경우 힌트를 사용하여 Spark에 명시적으로 브로드캐스트하도록 지시할 수 있습니다.
모범 사례:
대부분의 경우 Spark는 테이블을 브로드캐스트할 수 없을 때 정렬 병합 조인(SMJ)을 선택합니다. 정렬 병합 조인은 가장 비용이 많이 드는 방식입니다. 셔플 해시 조인(SHJ)은 경우에 따라 SMJ처럼 추가 정렬 단계가 필요하지 않기 때문에 더 빠를 수 있습니다. Spark에게 SMJ보다 SHJ를 선호한다고 알려주는 설정이 있습니다:
spark.conf.set("spark.sql.join.preferSortMergeJoin", "false")
Databricks Photon 엔진도 쿼리 성능을 향상시키기 위해 정렬 병합 조인을 셔플 해시 조인으로 대체합니다.
Spark SQL은 쿼리 계획을 개선하기 위해 비용 기반 최적화기(CBO)를 사용할 수 있습니다. 이는 여러 조인이 있는 쿼리에 특히 유용합니다. CBO는 기본적으로 활성화되어 있습니다.
CBO가 작동하려면 테이블 및 열 통계를 수집하고 최신 상태로 유지하는 것이 중요합니다. 통계를 기반으로 CBO는 가장 경제적인 조인 전략을 선택합니다. 다음 SQL 명령을 테이블에서 실행하여 통계를 계산해야 합니다:
CopyANALYZE TABLE my_table COMPUTE STATISTICS
ANALYZE TABLE my_table COMPUTE STATISTICS FOR COLUMNS col1, col2, col3
더 빠른 쿼리 실행을 위해 CBO는 ANALYZE TABLE 명령으로 계산된 통계를 사용하여 테이블을 조인해야 하는 최적의 순서를 찾을 수 있습니다(예: 작은 테이블을 먼저 조인하면 성능이 크게 향상됨). 조인 재정렬은 INNER 및 CROSS 조인에서만 작동합니다. 이 기능을 활용하려면 다음 구성을 설정하세요:
spark.conf.set("spark.sql.cbo.enabled", "true")
spark.conf.set("spark.sql.cbo.joinReorder.enabled", "true")
모범 사례:
Spark의 적응형 쿼리 실행(AQE)도 ANALYZE TABLE 명령으로 계산된 통계를 활용합니다. 따라서 테이블 통계를 최신 상태로 유지하기 위해 ANALYZE TABLE 명령을 정기적으로 실행하는 것이 좋습니다.
이상으로 첫 번째 블로그 글을 마치겠습니다. 다음 글에서는 데이터 스필링, 데이터 스큐 및 데이터 폭발 최적화 방법을 다룰 예정입니다.