🗝️ 키-값 형태의 RDD
- RDD에는 데이터를 키-값 형태로 다룰 수 있는 다양한 메서드가 있다
<연산명>ByKey
형태 → PairRDD 타입만 사용 가능
- PairRDD 타입은 RDD에 맵 연산을 수행해 키-값 구조로 만들 수 있다. 즉, 레코드에 두 개의 값이 존재한다.
KeyBy
: 키를 생성하는 함수
mapValues
, flatMapValues
: 값 매핑
keys
, values
: 키와 값 추출
lookup
: 특정 키에 관한 결과 찾기
sampleByKey
: 근사치나 정확도를 이용해 키를 기반으로 RDD 샘플을 생성
🥦 집계
- 사용하는 메서드에 따라 일반 RDD나 PairRDD를 사용해 집계를 수행할 수 있다.
countByKey
- 각 키의 아이템 수를 구하고 로컬 맵으로 결과를 수집
groupByKey
- 모든 익스큐터에서 함수를 적용하기 전에 해당 키와 관련된 값을 메모리로 읽어 들여야 한다는 문제점 존재 → OutOfMemoryError
reduceByKey
- 각 파티션에서 리듀스 작업을 수행하기 때문에 훨씬 안정적이며 모든 값을 메모리에 유지하지 않아도 된다.
- 최종 리듀스 작업을 제외한 모든 작업은 개별 워커에서 처리하기 때문에 연산 중에 셔플이 발생하지 않는다.
- 안정성 및 연산 수행 속도 향상
aggregate
- 드라이버에서 최종 집계를 수행하기 때문에 성능에 약간의 영향이 있다.
treeAggregate
- 드라이버에서 최종 집계를 수행하기 전에 익스큐터끼리 트리를 형성해 집계 처리의 일부 하위 과정을 '푸시 다운' 방식으로 먼저 수행한다.
aggregateByKey
combineByKey
- 집계 함수 대신 컴바이너를 사용한다.
- 컴바이너(combiner)는 키를 기준으로 연산을 수행하며 파라미터로 사용된 함수에 따라 결과값을 병합한 뒤 여러 컴바이너의 결과값을 병합해 결과를 반환한다.
foldByKey
- 결합 함수와 항등원인 '제로값'을 이용해 키의 값을 병합한다.
🥶 cogroup
- 스칼라의 경우 최대 3개, 파이선의 경우 최대 2개의 키-값 형태의 RDD를 그룹화할 수 있으며 각 키를 기준으로 값을 결합한다.
- 즉, RDD에 대한 그룹 기반의 조인을 수행한다.
- 출력 파티션 수나 클러스터에 데이터 분산 방식을 정확하게 제어하기 위해 사용자 정의 파티션 함수를 파라미터로 사용할 수 있다.
- 키-값 형태의 배열을 결과로 반환한다.
🥐 조인
- 구조적 API와 거의 동일한 조인 방식을 가지고 있지만, RDD를 사용하면 사용자가 많은 부분에 관여해야 한다.
- 조인하려는 두 개의 RDD가 기본적으로 필요하며, 때에 따라 출력 파티션 수나 사용자 정의 파티션 함수를 파라미터로 사용한다.
🤐 zip
- 동일한 길이의 두 개의 RDD를 지퍼를 잠그듯이 연결할 수 있으며, PairRDD를 생성한다.
- 두 개의 RDD는 동일한 수의 요소와 동일한 수의 파티션을 가져야 한다.
👯♀️ 파티션 제어하기
- RDD를 사용하면 데이터가 클러스터 전체에 물리적으로 정확히 분산되는 방식을 정의할 수 있다.
- 파티션 함수를 파라미터로 사용할 수 있다.
coalesce
: 파티션을 재분배할 때 발생하는 데이터 셔플을 방지하기 위해 동일한 워커에 존재하는 파티션을 합치는 메서드
repartition
: 파티션 수를 늘리거나 줄일 수 있지만, 처리 시 노드 간의 셔플이 발생할 수 있다.
repartitionAndSortWithinPartitions
: 파티션을 재분부핼 수 있고, 재분배된 결과 파티션의 정렬 방식을 지정할 수 있다.
👾 사용자 정의 파티셔닝
- RDD를 사용하는 가장 큰 이유 중 하나
- 사용자 정의 파티셔너는 저수준 API의 세부적인 구현 방식이며, 잡이 성공적으로 동작되는지 여부에 상당한 영향을 미친다.
- 예) 페이지랭크 → 사용자 정의 파티셔닝을 이용해 클러스터의 데이터 배치 구조를 제어하고 셔플을 회피한다.
- 데이터 치우침(skew) 같은 문제를 피하고자 클러스터 전체에 걸쳐 데이터를 균등하게 분배하는 목표를 갖는다.
- 사용자 정의 파티셔너를 사용하려면 구조적 API로 RDD를 얻고 사용자 정의 파티셔너를 적용한 다음 다시 DataFrame이나 Dataset으로 변환해야 한다.
- 사용자 정의 파티셔닝을 사용하려면 Partitioner를 확장한 클래스를 구현해야 한다.
HashPartitioner
, RangePartitioner
- 사용자 정의 키 분배 로직: RDD 수준에서만 사용할 수 있으며, 임의의 로직을 사용해 물리적인 방식으로 클러스터에 데이터를 분배하는 강력한 방법이다.
🔗 사용자 정의 직렬화
- Kryo 직렬화
- 자바 직렬화보다 약 10배 이상 성능이 좋으며 간결하다.
- 최상의 성능을 얻기 위해 프로그램에서 사용할 클래스를 사전에 등록해야 한다.
- 모든 직렬화 유형을 지원하진 않는다.
[출처] 스파크 완벽 가이드 (빌 체임버스, 마테이 자하리아 지음)