큰 데이터를 여러 대의 기계에 나눠 처리하기 위한 프로그래밍 모델이다. 2004년 구글 논문에서 제안됐고, 이를 오픈소스로 구현한 것이 Hadoop MapReduce 다.

처리를 두 함수로만 표현하게 강제하는 것이 핵심이다.

  • map: 입력 레코드 하나를 받아 (key, value) 쌍을 0개 이상 내놓는다
  • reduce: 같은 key 를 가진 value 들을 모아 하나로 줄인다

두 함수 사이에서 프레임워크가 같은 key 끼리 모아 정렬해 넘기는데, 이 단계를 shuffle 이라고 한다.

왜 이렇게 제한하나

map 함수는 레코드마다 독립적으로 동작하므로, 데이터를 조각내 여러 기계에서 동시에 돌릴 수 있다. reduce 도 key 별로 독립적이라 마찬가지다.

이 제약을 받아들이면 분산 처리에서 어려운 부분을 프레임워크가 대신 떠맡을 수 있다. 어느 기계에 무엇을 보낼지, 중간 결과를 어떻게 모을지, 기계 하나가 죽으면 그 조각만 어떻게 다시 실행할지를 사용자가 짜지 않아도 된다.

데이터가 있는 기계로 계산을 보내는 것도 이 모델의 전제다. 수 테라바이트를 네트워크로 옮기는 것보다 코드를 옮기는 편이 훨씬 싸기 때문에, HDFS 에 블록이 저장된 노드에서 map 을 실행한다.

단어 세기 예시

def map(doc_id, text):
    for word in text.split():
        emit(word, 1)
 
def reduce(word, counts):
    emit(word, sum(counts))

map 이 모든 단어에 대해 (단어, 1) 을 내놓고, shuffle 이 같은 단어를 한데 모으고, reduce 가 합친다.

한계와 그 이후

표현하기 불편하다. join, 정렬, 여러 단계 집계처럼 흔한 작업도 map 과 reduce 의 조합으로 옮겨야 한다. 단계가 여러 개면 MapReduce 작업을 여러 개 이어붙여야 하고, 그때마다 중간 결과가 HDFS 에 기록됐다가 다시 읽힌다. 디스크 왕복이 반복되어 느리다.

진입 장벽이 높다. 분석가나 기획자가 데이터를 보려면 자바 코드를 짜야 했다. 이 문제를 풀려고 페이스북이 만든 것이 Hive 로, SQL 로 질의를 쓰면 MapReduce 작업으로 번역해 준다.

중간 결과를 메모리에 유지해 디스크 왕복을 없앤 Apache Spark 가 나오면서 MapReduce 를 직접 쓰는 일은 크게 줄었다. 다만 map–shuffle–reduce 라는 뼈대는 Spark 를 포함한 이후 프레임워크에 그대로 남아 있다.