데이터 레이크하우스와 델타 레이크 최적화 가이드(1/3)

GarionNachal·2025년 4월 20일
0

databricks

목록 보기
18/24
post-thumbnail

소개

이 문서는 Databricks, Apache Spark 및 Delta Lake의 필수 모범 사례와 최적화 기술을 한곳에 모아 제공합니다. 모든 데이터 엔지니어와 데이터 아키텍트가 최적화되고 비용 효율적인 데이터 파이프라인을 설계하고 개발할 때 참고할 수 있는 가이드입니다. 비용은 사후 고려사항이 아닌 프로젝트 초기 단계부터 가장 중요한 비기능적 요구사항 중 하나로 다루어져야 합니다.

저자:

Himanshu Arora, Senior Resident Solutions Architect

Prashanth Babu Velanati Venkata, Lead Product Specialist

Delta Lake: 레이크하우스 포맷

Delta Lake는 데이터 레이크에 신뢰성, 보안 및 성능을 제공하는 오픈 포맷 스토리지 레이어입니다. Spark, PrestoDB, Flink, Trino, Hive와 같은 컴퓨팅 엔진과 Python, SQL, Scala, Java, Rust, Ruby용 API를 사용하여 레이크하우스 아키텍처를 구축할 수 있습니다. Delta는 Parquet, Avro, ORC 등 다른 오픈 포맷보다 많은 이점을 제공합니다:

  • Delta는 오픈소스 Parquet 포맷 위에 ACID 트랜잭션, 고성능 및 다양한 기능을 보장하는 프로토콜입니다. Delta는 오픈소스이며 전체 프로토콜은 여기에서 확인할 수 있습니다.
  • Delta Lake는 ACID 트랜잭션을 지원하고 배치 및 스트리밍 패러다임을 통합하여 증분 데이터에 대한 삭제/삽입/업데이트 트랜잭션을 단순화합니다.
  • Delta를 사용하면 시간 여행(time travel)이 가능하며 테이블의 특정 시점 스냅샷 버전을 읽을 수 있습니다.
  • Delta는 또한 효율적인 데이터 레이아웃, 인덱싱, 데이터 스키핑, 캐싱 등 다양한 성능 개선 기능을 제공합니다.

따라서 모든 이점을 활용하기 위해 Delta를 기본 데이터 레이크 스토리지 포맷으로 사용하는 것이 좋습니다. Databricks DBR 8.x 이상에서는 Delta Lake가 기본 포맷입니다.

기본 데이터 레이아웃

Delta 테이블 내부에는 데이터를 저장하는 Parquet 파일이 있습니다. 또한 Parquet 파일 옆에 Delta 트랜잭션 로그를 저장하는 __delta_log_ 서브디렉토리가 있습니다. 이러한 Parquet 파일의 크기는 쿼리 성능에 매우 중요합니다.

작은 파일 문제는 빅데이터 세계에서 잘 알려진 문제입니다. 테이블에 작은 파일이 너무 많으면 이러한 작은 파일을 열고 닫는 데 소요되는 시간으로 인해 읽기 지연 시간이 발생합니다.

이 문제를 방지하려면 파일 크기를 16MB에서 1GB 사이로 유지하는 것이 좋습니다. 이는 워크로드와 특정 사용 사례에 따라 사례별로 구성할 수 있습니다.

최적화 방법

1. OPTIMIZE 및 Z-order

OPTIMIZE는 파일을 압축하여 최대 1GB의 파일 크기를 얻으며, 이는 구성 가능합니다. 이 명령은 기본적으로 구성한 크기(또는 구성하지 않은 경우 기본 1GB)로 파일 크기를 조정하려고 시도합니다. OPTIMIZE 명령을 ZORDER와 결합할 수도 있는데, 이는 선택한 열을 기준으로 데이터를 물리적으로 정렬하거나 함께 배치합니다.

모범 사례:

  • Z-order에는 항상 높은 카디널리티 열(예: 주문 테이블의 customer_id)을 선택하세요.
  • Z-order에는 4개 이상의 열을 사용하지 마세요. 열이 너무 많으면 Z-order 효과가 저하됩니다.
  • OPTIMIZE 명령은 항상 별도의 작업 클러스터에서 실행하고 작업 자체의 일부로 실행하지 마세요.
  • OPTIMIZE 명령에는 컴퓨팅 최적화 인스턴스 계열이 권장됩니다.
  • OPTIMIZE(ZORDER 포함 또는 제외)는 다운스트림 쿼리 성능을 개선하기 위해 정기적으로(일일 또는 주간) 수행해야 합니다.

2. Auto optimize

Auto optimize는 이름에서 알 수 있듯이 Delta 테이블에 쓸 때 작은 파일을 자동으로 압축하며, 기본적으로 128MB의 파일 크기를 달성하려고 시도합니다. 다음 두 가지 기능이 포함됩니다:

Optimize Write: 실제 데이터를 기반으로 Apache Spark 파티션 크기를 동적으로 최적화하고 각 테이블 파티션에 대해 128MB 파일을 작성하려고 시도합니다.

Auto Compact: Spark 작업이 완료된 후 새 작업을 시작하여 파일을 추가로 압축하여 128MB 파일 크기를 달성할 수 있는지 확인합니다.

3. 파티셔닝

파티셔닝을 사용하면 파티션 열을 필터로 제공하거나 파티션 열을 조인하거나 파티션 열을 집계할 때 쿼리 속도를 높일 수 있습니다. 스캔 시 불필요한 데이터 파티션(하위 폴더)을 건너뛰는 데 도움이 됩니다.

모범 사례:

  • Databricks는 1TB 미만 크기의 테이블을 파티셔닝하지 않고 수집 시간 클러스터링이 자동으로 적용되도록 하는 것을 권장합니다.
  • 각 파티션의 데이터가 적어도 1GB 이상일 것으로 예상되는 경우에만 열로 파티셔닝하세요.
  • 항상 낮은 카디널리티 열(예: 연도, 날짜)을 파티션 열로 선택하세요.
  • Delta의 생성된 열 기능을 활용할 수도 있습니다.

4. 파일 크기 튜닝

자동 최적화(128MB) 또는 최적화(1GB)에서 타겟팅하는 기본 파일 크기가 적합하지 않은 경우 요구 사항에 따라 미세 조정할 수 있습니다. delta.targetFileSize 테이블 속성을 사용하여 대상 파일 크기를 설정할 수 있습니다.

또한 delta.tuneFileSizesForRewrites 테이블 속성을 켜서 이 작업을 Databricks에 위임할 수도 있습니다. 이 속성이 true로 설정되면 Databricks는 워크로드를 기반으로 파일 크기를 자동으로 조정합니다.

데이터 셔플링: 왜 발생하고 어떻게 제어할 수 있는가

데이터 셔플은 조인, 집계, 윈도우 연산 등과 같은 넓은 변환(wide transformations)의 결과로 발생합니다. 네트워크를 통해 작업자 노드 간에 데이터를 전송하기 때문에 비용이 많이 드는 프로세스입니다. 셔플을 완전히 제거하거나 셔플의 효율성과 속도를 향상시키기 위해 몇 가지 최적화 접근 방식을 사용할 수 있습니다.

1. 브로드캐스트 해시 조인

데이터 셔플링을 완전히 피하기 위해 조인되는 두 테이블 또는 DataFrame 중 하나(더 작은 것)를 브로드캐스트합니다. 테이블은 드라이버에 의해 브로드캐스트되어 모든 작업자 노드에 복사됩니다.

조인을 실행할 때 Spark는 자동으로 10MB 미만의 테이블을 브로드캐스트합니다. 그러나 더 큰 테이블을 브로드캐스트하도록 이 임계값을 조정할 수 있습니다:

spark.conf.set("spark.sql.autoBroadcastJoinThreshold", 104857600) // 100MB로 설정

쿼리에 있는 일부 테이블이 작은 테이블임을 알고 있는 경우 힌트를 사용하여 Spark에 명시적으로 브로드캐스트하도록 지시할 수 있습니다.

모범 사례:

  • 브로드캐스트 해시 조인은 전체 외부 조인(full outer join)에서는 지원되지 않습니다.
  • 메모리가 많은(32GB+) 드라이버를 실행하는 경우 브로드캐스트 임계값을 200MB와 같이 안전하게 높일 수 있습니다.
  • 항상 힌트나 PySpark 브로드캐스트 함수를 사용하여 더 작은 테이블을 명시적으로 브로드캐스트하세요.
  • 1GB보다 큰 테이블은 브로드캐스트하지 마세요.

2. 셔플 해시 조인으로 정렬 병합 조인 대체

대부분의 경우 Spark는 테이블을 브로드캐스트할 수 없을 때 정렬 병합 조인(SMJ)을 선택합니다. 정렬 병합 조인은 가장 비용이 많이 드는 방식입니다. 셔플 해시 조인(SHJ)은 경우에 따라 SMJ처럼 추가 정렬 단계가 필요하지 않기 때문에 더 빠를 수 있습니다. Spark에게 SMJ보다 SHJ를 선호한다고 알려주는 설정이 있습니다:

spark.conf.set("spark.sql.join.preferSortMergeJoin", "false")

Databricks Photon 엔진도 쿼리 성능을 향상시키기 위해 정렬 병합 조인을 셔플 해시 조인으로 대체합니다.

3. 비용 기반 최적화(CBO) 활용

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")

모범 사례:

  • CBO 최적화를 적절히 활용하려면 ANALYZE TABLE 명령을 정기적으로 실행해야 합니다(하루에 한 번 또는 데이터가 10% 이상 변경된 경우).
  • ANALYZE TABLE 명령을 작업의 일부로 실행하지 마세요. 별도의 작업으로 별도의 작업 클러스터에서 실행해야 합니다.
  • 단일 쿼리에서 많은 내부 조인 및/또는 교차 조인이 수행되는 경우 조인 재정렬을 활용하세요.

Spark의 적응형 쿼리 실행(AQE)도 ANALYZE TABLE 명령으로 계산된 통계를 활용합니다. 따라서 테이블 통계를 최신 상태로 유지하기 위해 ANALYZE TABLE 명령을 정기적으로 실행하는 것이 좋습니다.

이상으로 첫 번째 블로그 글을 마치겠습니다. 다음 글에서는 데이터 스필링, 데이터 스큐 및 데이터 폭발 최적화 방법을 다룰 예정입니다.

profile
AI를 꿈꾸는 BackEnd개발자

0개의 댓글