아파치 스파크 셔플링

From CS Wiki

아파치 스파크 셔플링(Shuffling)은 데이터가 스파크 클러스터 내의 다른 노드로 이동하여 재배치되는 과정이다. 셔플링은 주로 넓은 변환(Wide Transformation)을 수행할 때 발생하며, 데이터의 재분배가 필요한 연산에서 이루어진다. 이 과정에서 데이터를 파티션 간에 이동시키기 때문에 네트워크 비용과 디스크 I/O가 발생하고, 성능에 큰 영향을 미칠 수 있다.

1 개요[edit | edit source]

셔플링은 스파크에서 데이터를 분산 처리할 때 매우 중요한 연산이다. 데이터를 다른 파티션으로 이동시켜야 하는 경우, 예를 들어 groupByKey, reduceByKey, join과 같은 연산에서는 셔플링이 발생한다. 셔플링이 발생하는 이유는 데이터가 하나의 파티션에 집중되어 있지 않고, 여러 파티션에 분산되어야 하기 때문이다.

스파크에서는 셔플링을 최소화하고 성능을 최적화하려는 노력이 필요하며, 셔플 연산은 매우 비효율적일 수 있기 때문에 이를 어떻게 관리하느냐가 성능에 큰 영향을 미친다.

2 셔플링이 발생하는 연산[edit | edit source]

다음과 같은 연산에서 셔플링이 발생한다:

  • groupByKey: 같은 키에 속하는 데이터를 같은 파티션으로 모을 때 셔플링이 발생한다.
  • reduceByKey: 데이터를 합치기 위해 셔플링이 발생한다.
  • join: 서로 다른 RDD나 데이터프레임을 결합할 때 셔플링이 발생한다.
  • distinct: 중복된 데이터를 제거할 때 셔플링이 발생한다.
  • coalesce: 파티션을 합칠 때 셔플링이 발생할 수 있다.

이 연산들에서 셔플링은 필수적인 과정이며, 데이터가 물리적으로 재분배되기 때문에 성능에 큰 영향을 미친다.

3 셔플링의 장점과 단점[edit | edit source]

  • 장점 (용도)
    • 데이터를 효율적으로 재분배하여 각 파티션에서 병렬 처리가 가능해진다.
    • 여러 파티션에 걸쳐 분산된 데이터를 집합하거나 정렬할 수 있다.
  • 단점 (문제점)
    • 셔플링은 네트워크 I/O를 발생시키며, 이는 성능을 저하시킬 수 있다.
    • 디스크 I/O와 네트워크 대역폭을 많이 소비하므로, 셔플링이 많으면 성능이 크게 떨어질 수 있다.
    • 셔플링은 데이터 크기에 비례하여 비용이 커지기 때문에, 대규모 데이터셋에서의 셔플링은 매우 비용이 많이 들 수 있다.

4 셔플링을 최소화하는 방법[edit | edit source]

셰플링을 최소화하거나 효율적으로 처리하려면 몇 가지 방법을 고려할 수 있다:

  • 적절한 연산 사용: reduceByKey와 같은 연산은 groupByKey보다 효율적으로 데이터를 처리할 수 있다. reduceByKey는 파티션 내에서 먼저 값을 병합한 후 셔플링을 진행하기 때문에, 불필요한 데이터 이동을 줄일 수 있다.
  • 파티션 수 조정: 셔플링을 처리하는 과정에서 파티션 수를 조정하여 최적화할 수 있다. 예를 들어, 너무 많은 파티션이 존재하면 셔플링 비용이 증가할 수 있다.
  • persist 또는 cache 사용: 셔플링을 여러 번 반복해야 하는 경우, 중간 데이터를 캐싱하여 I/O를 줄일 수 있다.
  • shufflePartitions 설정: 스파크의 기본 셔플 파티션 수를 설정하여 성능을 최적화할 수 있다.

5 예시 1: groupByKey에서 셔플링 발생[edit | edit source]

아래 예시에서는 groupByKey 연산이 셔플링을 발생시키는 예를 보여준다. 각 키에 대해 값을 그룹화하려면, 데이터가 다른 파티션으로 이동해야 한다.

val rdd = sc.parallelize(Seq(("a", 1), ("b", 2), ("a", 3), ("b", 4)))

// groupByKey 사용: 셔플링 발생
val groupedRdd = rdd.groupByKey()

groupedRdd.collect().foreach(println)  // 출력: (a, [1, 3]), (b, [2, 4])

이 예시에서는 groupByKey가 각 키에 대해 값을 그룹화하기 위해 셔플링을 발생시킨다.

6 예시 2: reduceByKey에서 셔플링 최적화[edit | edit source]

reduceByKey는 groupByKey보다 효율적이며, 파티션 내에서 먼저 값을 병합한 후 셔플링을 수행한다.

val rdd = sc.parallelize(Seq(("a", 1), ("b", 2), ("a", 3), ("b", 4)))

// reduceByKey 사용: 셔플링 최적화
val result = rdd.reduceByKey((x, y) => x + y)

result.collect().foreach(println)  // 출력: (a, 4), (b, 6)

이 예시에서는 reduceByKey가 파티션 내에서 먼저 값을 병합한 후 셔플링을 최소화한다.

7 셔플링 최적화 기술[edit | edit source]

  • 빠른 셔플링 알고리즘: 최신 버전의 스파크에서는 더 빠른 셔플링 알고리즘을 사용하여 셔플링 성능을 개선하고 있다.
  • 전체 데이터를 정렬하여 셔플링: 셔플링을 수행하기 전에 데이터를 정렬하면, 셔플링 효율성을 높일 수 있다.
  • 중간 데이터 저장: 셔플링이 자주 발생하는 연산에서는 중간 데이터를 디스크나 메모리에 저장하고, 이를 반복적으로 사용할 수 있다.

8 같이 보기[edit | edit source]

  • 아파치 스파크
  • RDD(Resilient Distributed Dataset)
  • reduceByKey
  • groupByKey

9 참고 문헌[edit | edit source]

  • Zaharia, Matei, et al. "Spark: Cluster Computing with Working Sets." USENIX, 2010.
  • Chambers, Bill, and Zaharia, Matei. "Spark: The Definitive Guide." O'Reilly Media, 2018.