[t:/]$ 지식_

맵리듀스 파티션 몰빵 문제 해결

2022/08/02

배경

하둡 스트리밍을 쓰고 있다. 무슨무슨 설명은 길기 때문에 짧게 요약한다. 스팍에서 무슨무슨 문제가 있었고, MR 코딩은 자바에 의존한다. 하둡 스트리밍을 쓰면 아무 언어로 짜기가 쉽다. 스팍처럼 하이브 연동하기가 쉽다. 결정적으로 나는 MR을 C와 파이썬으로 짰다. 하둡 스트리밍이니까 쉽게 된다.

파티션 생성

하이브에서 파티셔닝은 고속 스캔의 힌트를 준다. 실제 구성은 단순히 hdfs상의 디렉토리 구조이다. 예를 들어 이렇다.

hdfs://ddd/part_dt=20220801/part-001

part_dt는 하이브상에서 컬럼명이며 파티션이다. where 절에서 part_dt 조건이 있다면 고속 스캔이 된다.

하둡 스트리밍에서 파티션

하둡 스트리밍에서 결과 파일은 output 디렉토리의 리듀서 번호가 된다. 즉, 다음과 같다.


hdfs://ddd/part-001
hdfs://ddd/part-002

결과적으로 파티션을 지정할 수 없다. part-001의 내용물은 이렇게 될 것이다.

20200801, 홍길동, 용산
20200801, 홍길똥, 옥수

리듀서

리듀서는 키를 보고 키의 해시값을 기준으로 나눈다. 하둡 스트리밍에서 키는 디폴트로 탭으로 구분된 첫 번째 컬럼이다. 위에서는 part_dt에 해당하는 20200801이 키가 된다.

리듀서의 갯수를 정할 수 있는데, 리듀서가 180개이고 360일치를 처리한다고 치자. 하나의 리듀서는 대충 이틀치를 모아서 처리한다. 내부적으로 소팅이 일어나고 이런 건 이미 이해하고 있다고 가정한다.

리듀서 몰빵

매일 대량의 마트 데이터가 말아지고 있는 상황이다. 마트 데이터는 최종일에만 저장된다. 즉, 365일중 어제 날짜에만 마트 데이터가 있다. 365일중 364일에는 데이터가 1기가씩 밖에 없는데 마지막 하루에 1테라가 있을 수 있다. 1테라를 처리하는 리듀서가 끝날때까지 잡이 계속된다. 느리다. 리듀서 업무는 나눌 수 없다. 누적적인 계산을 하는 로직이기 때문이다. 예를 들어 판교의 총 인구를 구한다고 치면 회사별로 나눠서 더할 수 있으나 각 회사의 인구를 전부 더하는 리듀서는 단 1개일 수 밖에 없다.

리듀서 몰빵 회피

여러 트릭이 존재한다. 예를 들어 랜덤 숫자로 가상키를 만들어 키로 쓰면 리듀서 몰빵이 회피된다. 이 때 문제는 우리가 리듀서에서 원래 하려던 업무가 있다. 날짜별 사용자 수를 카운트 한다고 가정하자. 키가 랜덤값이므로 한 파일에 날짜별로 데이터가 모여있지 않다. 리듀서 업무를 다른 곳에서 다시 할 경우 리듀서 갯수를 0으로 설정하면 맵퍼 작업만 시행한다. 그러면 상관없다. 성능도 좋으나 잔잔바리 파일이 많이 생긴다.

리듀서 몰빵 회피 잘하기

키를 두 개씩 잡으면 된다. part_dt와 함께 사용자 uuid를 키로 잡으면 어떨까. 사용자라는 단위는 몰빵 바이어스가 없는 데이터다. 사용자별 통계는 우리의 목적에 부합하므로 키를 두 개씩 잡으면 된다. 하둡 스트리밍이라면 이렇게 된다.

http://www.troot.co.kr/tech/20170718_%ED%95%98%EB%91%A1%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D%20%EC%84%B8%EC%BB%A8%ED%82%A4%20%EC%86%8C%ED%8C%85.md

세컨키 소팅을 하겠다는 이야기다. 이렇게 리듀서 몰빵을 회피했다. 원리는 이렇다. 키를 part_dt로만 잡으면 하루에 리듀서가 몰빵된다.

하둡 스트리밍에서 문제

하둡 스트리밍에서 파티셔닝 구조의 파일 패쓰를 만드는 것은 가능하다.

http://www.troot.co.kr/tech/20190425_map%20reduce%20%EA%B2%B0%EA%B3%BC%20%ED%8C%8C%EC%9D%BC%EC%9D%98%20%EB%AC%B8%EC%A0%9C.md

하둡 스트리밍에서 리듀서 몰빵의 문제

위 방식으로 파티셔닝을 정할 때, 키를 한 개만 쓰면 리듀서 몰빵이 여전히 발생한다. 키 한 개를 나눠서 하는 트릭들도 있는데, 오버라이트에 의한 데이터 유실이 발생하거나 오류를 일으키기 좋다. 세컨키 소팅을 적용하고 다음과 같이 해결한다.

package com.custom;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.io.Text;
 import org.apache.hadoop.mapred.lib.MultipleTextOutputFormat;

 public class CustomMultiOutputFormat extends MultipleTextOutputFormat<Text, Text> {
       @Override
       protected String generateFileNameForKeyValue(Text key, Text value, String leaf) {
             return new Path(key.toString().split("\t")[0], leaf).toString();
       }

       /**
        * We discard the key as per your requirement
        */
       @Override
       protected Text generateActualKey(Text key, Text value) {
             return new Text(key.toString().split(t")[1]); 
       }
 }

즉, 세컨키 방식으로 키를 두 개 받아서 앞에 키를 파티션으로 쓰고 두 번째 키만 기록하는 것. 여러 트릭을 시도했으나 이 방식이 유실도 오류도 없다. 자칫 실수 하면 사용자명이 파티션 파일명이 될 수 있다. 망한다. HDFS에 사용자 수 만큼의 파일을 생성할 것이다.

리듀서 레벨에서 파일명 정하기 = 2단 파티셔닝

-D stream.num.reduce.output.key.fields=2
-D mapred.text.key.partitioner.option=-k1,2

리듀서에서 출력할 값중 키를 몇 개로 할 것인지 정할 수 있다. 리듀서가 job=count, part_dt=20200801, uid=홍길동 .. 으로 데이터를 출력했다면, job, part_dt, uid 를 모두 키로 본다. 리듀서 출력단은 이미 리듀서 업무가 끝났으므로 키가 무슨 상관인가 싶은데 아까 CustomPath 클래스에서 받아 먹을 수 있다. 받아서 파일 패쓰를 적절히 구성하면 2단 파티셔닝이 완성된다.

귀찮아서 억지로 썼더니 나밖에 모르겠네. 질문은 초당 오만원, 아니 그냥 연봉 1.5억에 주식 별도. 이메일 주세요.









[t:/] is not "technology - root". dawnsea, rss