Apache Spark란?
💡 Apache Spark 는 대용량 데이터를 여러 대의 컴퓨터에 분산하여 동시에 빠르게 처리 할 수 있는 오픈소스 분산 컴퓨팅 엔진입니다.Spark는 분산 처리(Distributed Processing) 엔진입니다. 단일 서버로 10시간 걸리는 ETL 작업을 10대의 서버에 분산하면 1시간으로 줄어듭니다. Databricks는 이 Spark 위에 클러스터 관리, 모니터링, 최적화(Photon 엔진)를 추가한 관리형 플랫폼입니다. 현업에서 Spark를 써야 하는 가장 현실적인 이유는 데이터 크기 입니다. 단일 서버의 메모리(보통 64~256GB)에 담을 수 없는 데이터를 처리해야 할 때 Spark가 필요합니다. pandas로 처리하던 데이터가 수십 GB를 넘어가기 시작하면,
MemoryError가 발생하기 시작하고, 그때가 Spark로 전환해야 할 시점입니다. 반대로 데이터가 10GB 이하라면 pandas나 Polars가 오히려 빠를 수 있습니다 — Spark는 분산 환경의 오버헤드(네트워크, 직렬화)가 있기 때문입니다.
Spark의 아키텍처: Driver와 Executor
Spark는 Driver 와 Executor 라는 두 가지 역할로 구성됩니다.
출처: Apache Spark 공식 문서 — Cluster Mode Overview
💡 노드(Node)란? 클러스터를 구성하는 개별 컴퓨터(또는 가상 머신)를 말합니다. Driver가 실행되는 노드를 Driver Node, Executor가 실행되는 노드를 Worker Node 라고 부릅니다.
Spark의 핵심 개념: DataFrame
Spark에서 데이터를 다루는 가장 기본적인 구조는 DataFrame 입니다.💡 DataFrame 은 행(Row)과 열(Column)로 구성된 분산 데이터 구조입니다. 엑셀의 시트나 SQL 테이블과 비슷하게 생겼지만, 내부적으로는 여러 Executor에 데이터가 나뉘어(Partition) 저장되어 있습니다.
파티션(Partition) — 분산의 단위
💡 파티션(Partition) 이란 DataFrame의 데이터를 물리적으로 나눈 조각입니다. 각 Executor는 하나 이상의 파티션을 담당하여 병렬로 처리합니다.
DataFrame (1억 건)을 4개 파티션으로 나누어 4개 Worker가 동시에 병렬 처리합니다.
Transformation과 Action
Spark의 연산은 크게 Transformation(변환) 과 Action(실행) 으로 나뉩니다. 이 구분은 Spark의 지연 실행(Lazy Evaluation) 방식과 직접 관련됩니다.Transformation (변환) — “계획 세우기”
데이터를 어떻게 변환할지 계획만 세우고, 실제로 실행하지는 않습니다.Action (실행) — “계획 실행하기”
실제로 데이터를 처리하고 결과를 반환합니다. Action이 호출되면 그때서야 위의 모든 Transformation이 실행됩니다.💡 지연 실행(Lazy Evaluation)이란? Spark가 Transformation을 즉시 실행하지 않고, Action이 호출될 때까지 기다리는 전략입니다. 이렇게 하면 Spark가 전체 실행 계획을 먼저 최적화한 후 실행할 수 있어서, 불필요한 계산을 줄이고 성능을 크게 향상시킬 수 있습니다.
주요 Transformation과 Action 목록
Spark SQL — SQL로 분산 처리하기
Spark의 강력한 장점 중 하나는 SQL로도 분산 처리가 가능 하다는 것입니다. Python DataFrame API와 SQL을 자유롭게 혼합하여 사용할 수 있습니다.%sql 매직 커맨드를 사용하여 노트북 셀에서 직접 SQL을 실행할 수도 있습니다.
실습: 기본 데이터 처리
Databricks에서의 Spark
Databricks에서 Spark를 사용할 때 알아두면 좋은 점들을 정리하겠습니다.🆕 Databricks Runtime 18.x: 최신 Databricks Runtime은 Apache Spark 4.1.0 을 기반으로 하며, 성능 개선과 새로운 기능들이 포함되어 있습니다. 클러스터 생성 시 최신 Runtime을 선택하시면 최적의 성능을 경험하실 수 있습니다.
심화: Spark 내부 동작과 성능 최적화
이 섹션에서는 Spark의 내부 동작 원리와 프로덕션 환경에서의 성능 최적화 기법을 다룹니다. 대규모 데이터 처리에서 발생하는 실제 문제와 해결 방법을 이해하면, 데이터 파이프라인을 훨씬 효율적으로 설계하실 수 있습니다.Catalyst Optimizer — Spark의 두뇌
Spark가 빠른 이유 중 하나는 Catalyst Optimizer 라는 쿼리 최적화 엔진입니다. SQL이든 DataFrame API든, 모든 연산은 Catalyst를 거쳐 최적화된 실행 계획으로 변환됩니다.실행 계획 변환 과정
주요 최적화 규칙
Cost-Based Optimization (CBO)
Catalyst는 테이블 통계(행 수, 컬럼 분포, 데이터 크기)를 활용하여 비용 기반 으로 최적의 실행 계획을 선택합니다.⚠️ Gotcha: CBO는 통계가 최신 상태일 때만 효과적입니다. 대량 데이터 적재 후 ANALYZE를 실행하지 않으면, Catalyst가 잘못된 통계로 비효율적인 조인 전략을 선택할 수 있습니다. Delta Lake의 Predictive Optimization은 이를 자동화합니다.
Shuffle 메커니즘 심화
💡 Shuffle 이란 데이터를 키(key) 기준으로 재분배하는 과정입니다. GROUP BY, JOIN, ORDER BY 등의 연산에서 발생하며, 네트워크를 통해 데이터가 이동 하므로 Spark에서 가장 비용이 큰 연산입니다.
조인 전략 비교
⚠️ Gotcha — Broadcast Join 메모리: Broadcast되는 테이블은 각 Executor의 메모리에 적재 됩니다. 100MB 테이블을 20개 Executor에 브로드캐스트하면 총 2GB의 메모리를 사용합니다. 임계값을 너무 크게 설정하면 OOM(Out of Memory)이 발생할 수 있습니다.
Shuffle 파티션 수 튜닝
⚠️ Gotcha: 파티션 수가 너무 많으면 스케줄링 오버헤드 와 소형 파일 문제 가 발생합니다. 반대로 너무 적으면 각 Task가 처리하는 데이터가 많아져 OOM 이나 GC 지연 이 발생합니다. AQE를 활성화하면 이를 자동으로 조정합니다.
AQE (Adaptive Query Execution) 심화
💡 AQE 는 Spark 3.x부터 도입된 기능으로, 쿼리 실행 중에 런타임 통계를 수집하여 실행 계획을 동적으로 수정합니다. Databricks Runtime에서는 기본 활성화되어 있습니다.
AQE의 세 가지 핵심 기능
⚠️ Gotcha: AQE는 Shuffle 또는 Broadcast Exchange 경계 에서만 재최적화합니다. 단일 Stage 내에서는 초기 계획이 유지됩니다. 따라서 AQE가 있어도 ANALYZE TABLE로 통계를 수집하는 것이 여전히 중요합니다.
데이터 Skew 처리
💡 데이터 Skew(편향) 란 특정 키에 데이터가 집중되어, 하나의 파티션이 다른 파티션보다 훨씬 큰 상태를 말합니다. 예를 들어, 전체 주문의 40%가 “서울” 지역인 경우, GROUP BY 지역 시 “서울” 파티션이 병목이 됩니다.
Skew 진단 방법
해결 방법 1: AQE Skew Join (자동)
위에서 설명한 AQE의 Skew Join 최적화가 자동으로 처리합니다. Databricks에서는 기본 활성화되어 있으므로, 대부분의 경우 별도 조치 없이 해결됩니다.해결 방법 2: Skew Hint (수동)
해결 방법 3: Salting 기법 (수동)
Skew가 심한 키에 랜덤 접미사(salt)를 추가하여 데이터를 강제로 분산시킵니다.⚠️ Gotcha: Salting은 작은 테이블을 N배(salt 수)로 복제하므로 메모리를 많이 사용합니다. AQE의 자동 Skew 처리가 가능하면 먼저 시도하고, 효과가 부족할 때만 Salting을 사용하세요.
메모리 관리
Spark Executor의 메모리는 세 영역으로 나뉩니다. 메모리 구조를 이해하면 OOM 오류를 진단하고 해결하는 데 큰 도움이 됩니다.Executor 메모리 구조
spark.executor.memory (예: 8GB) 구성:Spill to Disk
Execution Memory가 부족하면 Spark는 데이터를 디스크로 Spill(유출) 합니다. Spill 자체는 OOM을 방지하는 안전 장치이지만, 디스크 I/O로 인해 성능이 크게 저하 됩니다.OOM 대응 체크리스트
⚠️ Gotcha — collect()의 위험:df.collect()는 전체 DataFrame 데이터를 Driver 한 대의 메모리 로 가져옵니다. 1억 건의 데이터를 collect하면 Driver가 OOM으로 죽습니다. 반드시.limit(),.head(),.take()등으로 필요한 데이터만 가져오세요.
⚠️ Gotcha — Python UDF 메모리: Python UDF(User Defined Function)는 JVM과 Python 프로세스 사이에 데이터를 직렬화/역직렬화(SerDe)합니다. 이 과정에서 메모리를 2배 이상 사용하며, 성능도 크게 저하됩니다. 가능하면 Spark 내장 함수 나 Pandas UDF(Arrow 기반)를 사용하세요.
정리
다음 문서에서는 Spark를 실행하는 컴퓨팅 리소스인 클러스터 의 종류와 설정 방법을 살펴보겠습니다.