Disk Spill
Apache Spark에서 디스크 스필(Disk Spill)이란, 메모리 내에서 처리하기 어려울 정도로 데이터가 많아졌을 때 데이터를 디스크(주로 임시 디렉터리)에 임시로 저장(“스필”)하여 처리를 이어가는 과정을 말함.
이는 Spark가 분산 환경에서 대용량 데이터를 다룰 때 발생할 수 있는 정상적인 현상이며, 메모리를 효율적으로 활용하기 위해 설계된 핵심 매커니즘 중 하나임.
디스크 스필의 작동 원리
1. 메모리 기반 연산의 한계
Spark는 RDD(Resilient Distributed Dataset)나 Dataset/DataFrame 연산 시 최대한 메모리에서 데이터를 처리하여 빠른 속도를 추구함.
그러나 다음과 같은 상황에서 메모리만으로는 처리가 어려워짐.
1-1. 작업(Task)에서 사용하는 데이터량이 워커 노드(Executor)의 가용 메모리보다 큰 경우
1-2. Shuffle 연산 후 메모리에 적재해 놓을 수 있는 데이터보다 실제 Shuffle 결과가 훨씬 큰 경우
1-3. 조인(Join), 정렬(Sort) 같은 메모리를 많이 사용하는 연산에서 입력 데이터가 많거나, 중간에 생성되는 임시 데이터(중간 집계 등)가 커지는 경우
이처럼 Spark의 연산 과정에서 처리해야 할 데이터가 메모리 용량을 초과할 때, Spark는 일부 데이터를 디스크에 “스필”하여 남은 메모리로 연산을 계속 진행하게 됨.
2. Shuffle Spill과 Sort Spill
Spark 디스크 스필은 크게 Shuffle Spill과 Sort Spill 형태로 나타남.
2-1. Shuffle Spill
Shuffle 단계에서 각 파티션의 일부나 전부를 디스크에 임시 저장함.
예를 들어, Shuffle 파일을 생성하고, 이후 Reduce 단계에서 이를 다시 읽어오는 과정에서 스필이 발생할 수 있음.
2-2. Sort Spill
SortMergeJoin, SortWithinPartitions, Window Functions, 정렬이 필요한 Aggregation 등에서 내부적으로 데이터를 정렬 또는 정렬 기반 알고리즘을 사용할 때, 정렬해야 할 데이터가 커서 메모리를 초과하면 디스크에 분할하여 임시로 저장함.
3. Spill 연산 과정의 핵심
스필 시 Spark의 주요 동작은 다음과 같음.
3-1. 정렬 기반 알고리즘 사용 (Sort-based shuffle 방식)
메모리에서 가능한 만큼 데이터를 정렬하거나 집계한 뒤, 한계를 넘으면 디스크에 기록(spill)함.
이렇게 여러 조각(runs)을 생성한 다음, 최종적으로 이들을 병합(merge)하며 전체 정렬 결과를 얻음.
3-2. Hash-based 알고리즘에서 해시 충돌 및 메모리 부족
해시맵(HashAggregate, HashJoin)으로 데이터를 처리할 때 메모리가 부족하면, 현재까지 처리한 해시맵 내용을 디스크에 스필하고, 다시 새로운 해시맵을 만들어 연산을 이어갑니다. 최종적으로 여러 개의 스필 파일을 순차적으로 merge합니다.
이러한 과정은 Spark 2.x 이후에 주로 쓰이는 Sort-based Shuffle 기법에서 더욱 강조됨.
해시를 사용하더라도 내부적으로 정렬 기반 기법과 유사하게 Spill & Merge 과정을 거침.
디스크 스필이 미치는 영향
1. 성능 저하
디스크는 메모리에 비해 입출력 속도가 매우 느림.
디스크 스필이 잦아지면, 디스크 I/O 비용이 증가하여 Spark 잡(Job) 전체 실행 시간이 길어지게 됨.
2. Shuffle 파일 증가
Shuffle Spill은 Shuffle 파일의 양을 증가시킴.
이 때 네트워크 I/O 비용도 커지고, Executor와 Driver가 관리해야 하는 파일 메타데이터도 늘어남.
3. GC(가비지 컬렉션) 부담 완화
디스크로 스필하는 과정 자체는 성능 저하를 유발하지만, 지나치게 큰 객체를 메모리에 유지하지 않도록 하여 GC 부담을 어느 정도 줄여 줄 수도 있음.
즉, 스필이 전혀 일어나지 않는 것이 항상 최적이라고 단정 지을 수만은 없고, 적절한 스필은 대용량 데이터를 처리하기 위한 불가피한 트레이드오프이기도 함.
디스크 스필이 발생하는 주요 시점
1. Shuffle 단계
맵(Map) 태스크가 완료되면 Shuffle 파일을 생성함.
Reduce 단계에서 맵 출력 파일을 집계하는 과정에서 필요에 따라 스필이 발생함.
2. Aggregation/Join
Hash 기반 연산 도중 데이터가 해시맵 한계를 넘어서면 스필이 발생할 수 있음.
Sort 기반 연산에서는 병합 정렬(merge sort)을 여러 번 수행하면서 스필이 발생하기도 함.
3. Window Function
Row 끼리의 순서나 윈도 범위(window frame) 처리를 위해 내부적으로 정렬이 진행될 때 스필이 발생할 수 있음.
디스크 스필 최적화 방법
1. 메모리 설정 최적화
1-1. spark.executor.memory, spark.driver.memory
각 Executor와 Driver 프로세스에 할당되는 메모리를 적절히 설정해줌.
1-2. spark.memory.fraction, spark.memory.storageFraction
Spark Execution 메모리와 Storage 메모리 사이의 비율을 조정해 메모리 활용도를 높일 수 있음.
1-3. spark.sql.shuffle.partitions (또는 RDD-based Shuffle의 경우 spark.default.parallelism)
파티션 수를 최적화하여 메모리 사용량을 조절함.
2. 데이터 스큐(Skew) 해결
데이터가 특정 파티션에 과도하게 몰릴 경우, 해당 파티션으로 인해 스필이 빈번히 발생함.
스큐가 심한 키를 분산하기 위해 salting(키 뒤에 임의 값 추가), 스큐 조인 스크립트 등을 사용하거나, Spark 3.x의 Adaptive Query Execution(AQE) 기능을 활용하여 동적으로 파티션을 재조정(skew join splitting)할 수 있음.
3. Broadcast Join 활용
조인 대상 데이터 중 한 쪽이 작다면, Broadcast Join을 사용해 디스크 스필을 줄일 수 있음.
spark.sql.autoBroadcastJoinThreshold 설정값을 통해 자동 Broadcast가 가능한 임계값을 조정할 수 있음.
4. 코덱(압축) 및 I/O 최적화
4-1. spark.shuffle.compress, spark.shuffle.spill.compress
Shuffle 및 Spill 데이터의 압축 여부 설정
4-2. spark.io.compression.codec
Snappy, LZ4 등 다양한 코덱을 설정할 수 있으며, 작업 부하와 압축률에 따라 성능이 달라짐.
5. 오버헤드 메모리(spark.executor.memoryOverhead) 확보
Executor 메모리 설정 시, JVM Heap 외에 필요한 오버헤드 메모리도 충분히 크게 잡아야 함.
파이썬 UDF 또는 Arrow 등을 사용할 경우 오버헤드 메모리가 적절히 있어야 디스크 스필을 줄일 수 있음.
스필 발생 모니터링 및 진단
1. Spark UI
Storage 탭: RDD, DataFrame 캐싱에 사용된 메모리/디스크 사용량 확인
SQL 탭: 각 스테이지별 입력/출력(Shuffle Read/Write) 크기와 Spill 발생 여부 확인
Environment 탭: 메모리 설정, Shuffle 관련 설정 등을 점검
2. 실행 로그(Logs)
Driver 로그, Executor 로그에서 “Spilling”이라는 키워드가 포함된 메시지를 확인할 수 있습니다.
스필로 인해 I/O가 과도하게 늘어나는 로그가 있는지 확인하고, 어떤 스테이지나 파티션에서 발생했는지 추적합니다.
3. Metrics 시스템
Prometheus, Grafana와 같은 모니터링 시스템을 연동하여 Executor의 디스크 I/O, GC 시간, CPU 사용률 등의 변화를 추적함으로써 스필로 인한 부하를 모니터링할 수 있음.
정리
디스크 스필은 Spark가 대규모 데이터를 메모리 한계 안에서 안정적으로 처리하기 위한 핵심 안전장치임.
다만 디스크 I/O가 발생하기 때문에 성능 저하의 원인이 될 수 있어, 다음과 같은 점들을 숙지해야 함.
메모리와 파티션 설정을 적절히 조정하고,
데이터 스큐를 방지하며,
Broadcast Join 등 적합한 조인 전략을 활용하고,
압축 및 코덱 설정을 통해 스필 I/O 비용을 최소화하고,
Spark UI 및 로그를 모니터링하여 스필이 빈번하게 발생하는 지점을 찾아내는 것이 중요함.
이러한 최적화를 통해 디스크 스필의 부작용(과도한 디스크 I/O)을 최대한 줄이면서도, Spark가 제공하는 대규모 데이터 처리 능력을 안정적으로 활용할 수 있음.
특히 Spark 3.x에서 제공되는 Adaptive Query Execution(AQE), 동적 파티션 프루닝, 코스트 기반 최적화(CBO) 등의 기능을 적극적으로 도입하면 스필 가능성을 낮추고 자원 활용도를 더욱 개선할 수 있음.
결론적으로, “디스크 스필은 피해야 할 대상이 아니라 데이터가 많을 때 생기는 자연스러운 현상이며, 이를 최소화하고 적절히 관리하는 것이 핵심”이라고 할 수 있음.
Spark 환경에서 디스크 스필 최적화는 전체 파이프라인 성능과 리소스 관리의 균형을 맞추는 데 필수적인 요소임.
'Data Engineering > Spark' 카테고리의 다른 글
[Spark] Apache Spark 개념 (0) | 2025.03.30 |
---|---|
[Spark] Pyspark에서 repartition(), coalesce() 사용 (0) | 2025.03.29 |
[Spark] SparkSQL의 Window함수 종류 (1) | 2025.03.22 |
[Spark] Apache Spark 구조 (2) | 2025.03.15 |
[Spark] Apache Iceberg (2) | 2025.03.14 |
Disk Spill
Apache Spark에서 디스크 스필(Disk Spill)이란, 메모리 내에서 처리하기 어려울 정도로 데이터가 많아졌을 때 데이터를 디스크(주로 임시 디렉터리)에 임시로 저장(“스필”)하여 처리를 이어가는 과정을 말함.
이는 Spark가 분산 환경에서 대용량 데이터를 다룰 때 발생할 수 있는 정상적인 현상이며, 메모리를 효율적으로 활용하기 위해 설계된 핵심 매커니즘 중 하나임.
디스크 스필의 작동 원리
1. 메모리 기반 연산의 한계
Spark는 RDD(Resilient Distributed Dataset)나 Dataset/DataFrame 연산 시 최대한 메모리에서 데이터를 처리하여 빠른 속도를 추구함.
그러나 다음과 같은 상황에서 메모리만으로는 처리가 어려워짐.
1-1. 작업(Task)에서 사용하는 데이터량이 워커 노드(Executor)의 가용 메모리보다 큰 경우
1-2. Shuffle 연산 후 메모리에 적재해 놓을 수 있는 데이터보다 실제 Shuffle 결과가 훨씬 큰 경우
1-3. 조인(Join), 정렬(Sort) 같은 메모리를 많이 사용하는 연산에서 입력 데이터가 많거나, 중간에 생성되는 임시 데이터(중간 집계 등)가 커지는 경우
이처럼 Spark의 연산 과정에서 처리해야 할 데이터가 메모리 용량을 초과할 때, Spark는 일부 데이터를 디스크에 “스필”하여 남은 메모리로 연산을 계속 진행하게 됨.
2. Shuffle Spill과 Sort Spill
Spark 디스크 스필은 크게 Shuffle Spill과 Sort Spill 형태로 나타남.
2-1. Shuffle Spill
Shuffle 단계에서 각 파티션의 일부나 전부를 디스크에 임시 저장함.
예를 들어, Shuffle 파일을 생성하고, 이후 Reduce 단계에서 이를 다시 읽어오는 과정에서 스필이 발생할 수 있음.
2-2. Sort Spill
SortMergeJoin, SortWithinPartitions, Window Functions, 정렬이 필요한 Aggregation 등에서 내부적으로 데이터를 정렬 또는 정렬 기반 알고리즘을 사용할 때, 정렬해야 할 데이터가 커서 메모리를 초과하면 디스크에 분할하여 임시로 저장함.
3. Spill 연산 과정의 핵심
스필 시 Spark의 주요 동작은 다음과 같음.
3-1. 정렬 기반 알고리즘 사용 (Sort-based shuffle 방식)
메모리에서 가능한 만큼 데이터를 정렬하거나 집계한 뒤, 한계를 넘으면 디스크에 기록(spill)함.
이렇게 여러 조각(runs)을 생성한 다음, 최종적으로 이들을 병합(merge)하며 전체 정렬 결과를 얻음.
3-2. Hash-based 알고리즘에서 해시 충돌 및 메모리 부족
해시맵(HashAggregate, HashJoin)으로 데이터를 처리할 때 메모리가 부족하면, 현재까지 처리한 해시맵 내용을 디스크에 스필하고, 다시 새로운 해시맵을 만들어 연산을 이어갑니다. 최종적으로 여러 개의 스필 파일을 순차적으로 merge합니다.
이러한 과정은 Spark 2.x 이후에 주로 쓰이는 Sort-based Shuffle 기법에서 더욱 강조됨.
해시를 사용하더라도 내부적으로 정렬 기반 기법과 유사하게 Spill & Merge 과정을 거침.
디스크 스필이 미치는 영향
1. 성능 저하
디스크는 메모리에 비해 입출력 속도가 매우 느림.
디스크 스필이 잦아지면, 디스크 I/O 비용이 증가하여 Spark 잡(Job) 전체 실행 시간이 길어지게 됨.
2. Shuffle 파일 증가
Shuffle Spill은 Shuffle 파일의 양을 증가시킴.
이 때 네트워크 I/O 비용도 커지고, Executor와 Driver가 관리해야 하는 파일 메타데이터도 늘어남.
3. GC(가비지 컬렉션) 부담 완화
디스크로 스필하는 과정 자체는 성능 저하를 유발하지만, 지나치게 큰 객체를 메모리에 유지하지 않도록 하여 GC 부담을 어느 정도 줄여 줄 수도 있음.
즉, 스필이 전혀 일어나지 않는 것이 항상 최적이라고 단정 지을 수만은 없고, 적절한 스필은 대용량 데이터를 처리하기 위한 불가피한 트레이드오프이기도 함.
디스크 스필이 발생하는 주요 시점
1. Shuffle 단계
맵(Map) 태스크가 완료되면 Shuffle 파일을 생성함.
Reduce 단계에서 맵 출력 파일을 집계하는 과정에서 필요에 따라 스필이 발생함.
2. Aggregation/Join
Hash 기반 연산 도중 데이터가 해시맵 한계를 넘어서면 스필이 발생할 수 있음.
Sort 기반 연산에서는 병합 정렬(merge sort)을 여러 번 수행하면서 스필이 발생하기도 함.
3. Window Function
Row 끼리의 순서나 윈도 범위(window frame) 처리를 위해 내부적으로 정렬이 진행될 때 스필이 발생할 수 있음.
디스크 스필 최적화 방법
1. 메모리 설정 최적화
1-1. spark.executor.memory, spark.driver.memory
각 Executor와 Driver 프로세스에 할당되는 메모리를 적절히 설정해줌.
1-2. spark.memory.fraction, spark.memory.storageFraction
Spark Execution 메모리와 Storage 메모리 사이의 비율을 조정해 메모리 활용도를 높일 수 있음.
1-3. spark.sql.shuffle.partitions (또는 RDD-based Shuffle의 경우 spark.default.parallelism)
파티션 수를 최적화하여 메모리 사용량을 조절함.
2. 데이터 스큐(Skew) 해결
데이터가 특정 파티션에 과도하게 몰릴 경우, 해당 파티션으로 인해 스필이 빈번히 발생함.
스큐가 심한 키를 분산하기 위해 salting(키 뒤에 임의 값 추가), 스큐 조인 스크립트 등을 사용하거나, Spark 3.x의 Adaptive Query Execution(AQE) 기능을 활용하여 동적으로 파티션을 재조정(skew join splitting)할 수 있음.
3. Broadcast Join 활용
조인 대상 데이터 중 한 쪽이 작다면, Broadcast Join을 사용해 디스크 스필을 줄일 수 있음.
spark.sql.autoBroadcastJoinThreshold 설정값을 통해 자동 Broadcast가 가능한 임계값을 조정할 수 있음.
4. 코덱(압축) 및 I/O 최적화
4-1. spark.shuffle.compress, spark.shuffle.spill.compress
Shuffle 및 Spill 데이터의 압축 여부 설정
4-2. spark.io.compression.codec
Snappy, LZ4 등 다양한 코덱을 설정할 수 있으며, 작업 부하와 압축률에 따라 성능이 달라짐.
5. 오버헤드 메모리(spark.executor.memoryOverhead) 확보
Executor 메모리 설정 시, JVM Heap 외에 필요한 오버헤드 메모리도 충분히 크게 잡아야 함.
파이썬 UDF 또는 Arrow 등을 사용할 경우 오버헤드 메모리가 적절히 있어야 디스크 스필을 줄일 수 있음.
스필 발생 모니터링 및 진단
1. Spark UI
Storage 탭: RDD, DataFrame 캐싱에 사용된 메모리/디스크 사용량 확인
SQL 탭: 각 스테이지별 입력/출력(Shuffle Read/Write) 크기와 Spill 발생 여부 확인
Environment 탭: 메모리 설정, Shuffle 관련 설정 등을 점검
2. 실행 로그(Logs)
Driver 로그, Executor 로그에서 “Spilling”이라는 키워드가 포함된 메시지를 확인할 수 있습니다.
스필로 인해 I/O가 과도하게 늘어나는 로그가 있는지 확인하고, 어떤 스테이지나 파티션에서 발생했는지 추적합니다.
3. Metrics 시스템
Prometheus, Grafana와 같은 모니터링 시스템을 연동하여 Executor의 디스크 I/O, GC 시간, CPU 사용률 등의 변화를 추적함으로써 스필로 인한 부하를 모니터링할 수 있음.
정리
디스크 스필은 Spark가 대규모 데이터를 메모리 한계 안에서 안정적으로 처리하기 위한 핵심 안전장치임.
다만 디스크 I/O가 발생하기 때문에 성능 저하의 원인이 될 수 있어, 다음과 같은 점들을 숙지해야 함.
메모리와 파티션 설정을 적절히 조정하고,
데이터 스큐를 방지하며,
Broadcast Join 등 적합한 조인 전략을 활용하고,
압축 및 코덱 설정을 통해 스필 I/O 비용을 최소화하고,
Spark UI 및 로그를 모니터링하여 스필이 빈번하게 발생하는 지점을 찾아내는 것이 중요함.
이러한 최적화를 통해 디스크 스필의 부작용(과도한 디스크 I/O)을 최대한 줄이면서도, Spark가 제공하는 대규모 데이터 처리 능력을 안정적으로 활용할 수 있음.
특히 Spark 3.x에서 제공되는 Adaptive Query Execution(AQE), 동적 파티션 프루닝, 코스트 기반 최적화(CBO) 등의 기능을 적극적으로 도입하면 스필 가능성을 낮추고 자원 활용도를 더욱 개선할 수 있음.
결론적으로, “디스크 스필은 피해야 할 대상이 아니라 데이터가 많을 때 생기는 자연스러운 현상이며, 이를 최소화하고 적절히 관리하는 것이 핵심”이라고 할 수 있음.
Spark 환경에서 디스크 스필 최적화는 전체 파이프라인 성능과 리소스 관리의 균형을 맞추는 데 필수적인 요소임.
'Data Engineering > Spark' 카테고리의 다른 글
[Spark] Apache Spark 개념 (0) | 2025.03.30 |
---|---|
[Spark] Pyspark에서 repartition(), coalesce() 사용 (0) | 2025.03.29 |
[Spark] SparkSQL의 Window함수 종류 (1) | 2025.03.22 |
[Spark] Apache Spark 구조 (2) | 2025.03.15 |
[Spark] Apache Iceberg (2) | 2025.03.14 |