티스토리 뷰


맵리듀스 개요

맵리듀스(MapReduce)는 기존 하드웨어를 활용한 분산 프로그래밍 모델로서, 

대용량 데이터를 빠르고 안전하기 처리하기 위해 만들었다. 


2014년 OSDI 컨퍼런스에서 구글이 "MapReduce : Simplified Data Processing on Large Clusters" 논문을 발표한 이후, 

맵리듀스는 관심을 받기 시작했다. 


그리고 오픈소스 루씬(Lucene)의 개발자인 더그 커팅(Doug Cutting)이 하둡(Hadoop)을 만들면서 맵리듀스가 널리 알려졌다. 

하둡 오픈 소스 프로젝트는 구글의 분산 기술(GFS, MapReduce)을 기반으로 2006년부터 시작했다. 


하둡 파일 시스템(HDFS)는 대규모 분산 파일 시스템 구축의 성능과 안전정을 보여줬고, 

맵리듀스는 HDFS에 저장된 대규모 분산 파일에 대한 로그 분석, 색인 구축, 검색에 탁월한 능력을 발휘했다. 


이후, 아마존은 맵리듀스 플랫폼(Amazon Elastic MapReduce)을 클라우드 서비스로 제공하기 시작했고, 

몽고디비(MongoDB)와 같은 NoSQL에서도 MapReduce Operation을 제공했다. 


맵리듀스 - 발상의 전환

맵리듀스의 개념을 이해하기 위해, 먼저 기존 프로그래밍 방식을 생각해보자. 

첫째, 데이터를 가져온다. (Fetch Data)

둘째, 가져온 데이터를 처리한다. (Process Data)

셋째, 처리한 데이터를 저장한다. (Save Data)

기존 프로그래밍은 데이터를 가져와서 중앙에서 처리하고 다시 저장하는 구조다. 

하지만 대용량 데이터를 애써 분산 환경에 저장했는데, 처리를 위해 대용량 데이터를 다시 가져와야 한다니? 

즉, 데이터를 가져오는 비용이 많이 든다. 

그래서 데이터를 가져오지 않고 처리할 수 있는 '무엇'이 필요했다. 


이것이 바로 "맵리듀스"이다. 

발상 전환으로 데이터를 가져오지 않고, 데이터가 저장된 곳에서 처리할 수 있도록 만들었다. 

맵리듀스는 크게 맵(Map) 단계와 리듀스(Reduce) 단계로 나눈다. 

맵 단계가 바로 데이터가 저장된 로컬에서 동작한다. 


맵리듀스 이해 

맵 단계와 리듀스 단계 모두 입/출력으로 키-값(Key-Value) 쌍을 가진다. 

키와 값의 타입은 개발자가 선택하며, 맵 함수와 리듀스 함수도 개발자가 직접 작성해야 한다. 


맵리듀스의 단어수 계산(WordCount) 예제로 개념을 정리해 보자. 

"클라우드 컴퓨팅 용어의 정의는 무엇인가?"

"최근 클라우드 컴퓨팅이 화두가 되면서 애매모호한 용어로 인해"


위 두 문장의 단어를 세는 프로그램을 맵리듀스로 처리하면 다음과 같다. 



맵 함수의 입력은 다음과 같다 - 키: 줄번호(라인번호), 값: 문장(라인)

실제 키는 줄번호가 아니라, 파일 각 줄의 시작 오프셋(offset)이 된다. 


맵 함수에서는 각 문장을 공백으로 분류해 단어와 글자 수를 키-값 형태로 제공한다. 

그리고 정렬과 병합이 일어난다. 

여기까지가 분산 저장된 로컬 서버에서 일어나는 작업이다. 


리듀스 함수는 "단어-글자수 목록"을 입력으로 받아서, 각 단어들의 개수를 취합해 최종 결과를 제공한다. 


맵리듀스 구조

맵리듀스의 처리 과정을 한번 더 정리해 보자. 

대용량 입력 파일을 분리(Split)한 후, 맵 함수를 적용한다. 

그리고 리듀스 함수에 전달하기 위해 셔플링(Shuffling)이 일어나고 최종 결과를 생성한다. 


하둡에서 최초 입력 파일은 먼저 스플릿(Split)해서 HDFS에 저장한다. 

대용량 파일을 한번에 처리할 수 없기 때문에, 적절한 크기로 잘라낸 후 맵리듀스로 처리하는 것이다. 

하지만 스플릿의 크기가 너무 작을 경우, 분할 관리 및 맵 태스트 생성의 오버헤드가 있어 역효과가 날 수 있다. 

보통 하둡에서 64MB 이상의 스플릿을 사용하도록 권장하고 있다. 

클라우데라의 CDH에서는 기본값이 128MB로 설정되어 있다. 


셔플링(Shuffling)은 맵 함수의 결과를 취합하기 위해 리듀스 함수로 데이터를 전달하는 역할을 한다. 

그 전에 맵 함수의 결과에 대해 정렬과 병합(Sort & Merge)이 일어난다. 

그리고 각 서버에 나뉜 데이터들을 키를 중심으로 합쳐서 리듀스 함수에 전달한다. 

이렇게 리듀스 함수로 데이터를 전달하는 것을 셔플링(Shuffling)이라고 한다. 


이제 파티셔너(Partitioner)와  컴바이너(Combiner)에 대해서 살펴보자. 

파티셔너는 맵 함수의 결과를 각 파티션으로 나누어 저장하는 역할을 한다. 

파티션을 나누는 기준은 키이며, 기본적으로 키에 해쉬 함수를 적용해 처리한다. 

위 그림에서도 맵 결과를 버퍼에 저장하다가, 디스크에 저장할 때 파티션, 정렬 등의 작업이 일어나는 것을 알 수 있다. 


컴바이너는 일반적으로 리듀서와 동일하게 사용한다. 

그래서 컴바이너를 "로컬 리듀서"라고도 한다.

왜 컴바이너가 필요할까?

맵리듀스는 대용량 처리를 위해 만들었다. 

맵 함수까지는 로컬에서 실행하지만 리듀스 함수로 데이터를 전달하는 것은 네트워크를 이용할 수 밖에 없다. 

로컬에서 리듀스 함수를 먼저 처리한다면, 네트워크로 전달하는 데이터 양이 대폭 감소할 것이다. 


리듀스 함수를 컴바이너로 사용하기 위해서, 리듀스 함수의 입력과 출력의 타입이 같아야 한다. 

그렇지 않을 경우, 별도의 컴바이너를 구현해서 활용할 수 있다. 


컴바이너 적용에 따른 차이는 다음 그림을 살펴보도록 하자. 

컴바이너를 적용함으로써 리듀스 함수로 전달하는 데이터 양이 조금 줄어든 것을 볼 수 있다. 

"뭐 이정도 쯤이야?"하고 생각할 수 있지만, 실제 대용량을 처리할 때 그 차이는 무시할 수 없을 것이다. 


빅데이터를 다룬다면, 맵리듀스의 개념과 구조 정도 이해해야 한다. 

맵리듀스 대신 Hive와 같은 기술로 처리하더라도, 개발자라면 내부적으로 어떤 일이 일어나는지 알아야 하지 않을까?


-- 2012년 2월 29일 처음 작성한 글을 재구성했습니다. 


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함