Airflow & PySpark 개념 정리
Airflow
Airflow에서 모든 워크플로우는 DAG로 표현됩니다. DAG는 순환이 없는 방향 그래프이며, 특정 Task가 다른 Task보다 먼저 수행되어야 한다면 Directed Edge로 순서를 정의합니다.
주요 구성 요소는 다음과 같습니다.
-
DAG (Directed Acyclic Graph): 워크플로우 전체 구조를 정의하는 그래프로, 태스크 간의 실행 순서와 의존 관계를 나타냅니다. DAG는 순환이 없는 방향성 그래프로, 각 노드는 태스크를 나타내며, 엣지를 통해 실행 순서를 결정합니다.
-
Task: DAG 내의 개별 실행 단위로, 실제 작업(예: 데이터 처리, 스크립트 실행 등)을 수행합니다.
-
Operator: 특정 작업을 실행하기 위한 템플릿 클래스입니다. 예를 들어, BashOperator, PythonOperator, SparkSubmitOperator 등이 있으며, 이를 통해 표준화된 작업 실행 방식을 제공합니다.
-
Sensor: 특정 조건(예: 파일 존재, 특정 이벤트 발생 등)이 충족될 때까지 대기하는 특수한 태스크입니다.
-
Scheduler: DAG 파일을 주기적으로 스캔하여 태스크를 스케줄링하고 실행 순서를 결정하는 컴포넌트입니다.
-
Executor: 태스크를 실제로 실행하는 백엔드로, LocalExecutor(단일 머신), CeleryExecutor(분산 환경), KubernetesExecutor(컨테이너 기반 실행) 등이 있습니다.
PySpark
Apache Spark의 파이썬 인터페이스로, 대규모 데이터 처리와 분산 컴퓨팅을 지원합니다.
Architecture
Spark 아키텍처는 다음과 같습니다.
- Driver: 애플리케이션의 메인 프로세스로, 전체 작업의 제어와 스케줄링을 담당합니다.
- Executors: 분산 환경에서 실제 작업을 실행하는 워커 프로세스입니다.
- Cluster Manager: 클러스터 내 자원(메모리, CPU 등)을 관리하며, Spark Standalone, YARN, Mesos, Kubernetes 등 다양한 옵션이 있습니다.
lazy evaluation
Spark는 실제 연산을 바로 수행하지 않고, Transformation을 적용해 DAG(작업 그래프)를 구성한 후, Action이 호출될 때 전체 DAG를 최적화하여 실행합니다. 이로 인해 불필요한 계산을 줄이고 효율적인 실행 계획을 세울 수 있습니다.
Transformation vs. Action
- Transformation: 새로운 RDD나 DataFrame을 생성하는 연산으로, map, filter, join 등이 있으며, 실제 실행은 지연됩니다.
- Action: 실제 결과를 반환하거나 데이터를 저장하는 연산으로, count, collect, save 등이 있습니다. Action 호출 시 DAG가 실행됩니다.
PySpark 작업 성능 최적화를 위한 전략(예: 파티션 수 조절, 셔플 최소화, 데이터 스키마 최적화 등)
- 데이터를 여러 파티션으로 분할하여 작업을 병렬 처리합니다. 파티션 수를 적절히 조절하여 오버헤드를 줄이고, 병렬성을 극대화할 수 있습니다.
- 셔플 최소화: 불필요한 데이터 이동(셔플)을 줄이기 위해 연산 순서를 최적화하고,
repartition()
,coalesce()
를 사용합니다. - 추가 최적화: 데이터 스키마 최적화, predicate pushdown, 캐시 활용 등 다양한 기법을 통해 작업 성능을 향상시킬 수 있습니다.
파티셔닝
-
repartition()
: 파티션 수를 증가/감소할 때 사용하며, 풀 셔플(Full Shuffle) 발생. -
coalesce()
: 파티션 수를 감소할 때 사용하며, 셔플 없이 인접 파티션을 병합 (성능 효율적). - Too Few Partitions: 병렬성 감소 → 리소스 미활용.
- Too Many Partitions: 태스크 오버헤드 증가 → 스케줄링 지연.
데이터 스큐(Data Skew) 문제
- Salting: 임의의 키를 추가해 데이터 분산
- 파티션 수 조절: 파티션 수를 적절히 조절하여 데이터 분산을 최적화
데이터 크기
- 권장: 파티션 당 128MB~1GB (HDFS 블록 크기 참고).
- 계산식: 파티션 수 ≈ (데이터 총 크기) / (128MB)
클러스터 리소스
- 코어 수: 파티션 수 ≥ 총 코어 수 × 2 (예: 100코어 → 200파티션).
- 메모리: 파티션 크기가 Executor 메모리를 초과하지 않도록 주의.
메모리 관리 및 캐싱
- 메모리 관리: Spark는 데이터를 분산 메모리에 저장하여 빠른 연산을 가능하게 합니다.
- 캐싱/Persistence:
cache()
,persist()
메서드를 사용하여 자주 재사용되는 데이터셋을 메모리에 저장함으로써, 반복 연산 시 재계산 비용을 절감할 수 있습니다. 이는 작업 성능을 크게 향상시키는 중요한 최적화 기법입니다.
Enjoy Reading This Article?
Here are some more articles you might like to read next: