ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 맵리듀스와 분산 파일 시스템
    책/데이터 중심 어플리케이션 설계 2024. 6. 27. 13:57
    맵 리듀스(MapReduce)는 구글에서 개발한 대용량 데이터 처리를 위한 분산 컴퓨팅 모델이다. 이 모델은 주로 빅데이터를 분석하고 처리하는 데 사용된다. 맵 리듀스는 두 가지 주요 단계로 구성돠는데 크게 맵(Map) 단계와 리듀스(Reduce) 단계이다.

     

    먼저, 맵(Map) 단계에서는 입력 데이터를 여러 조각으로 나누어 각 조각을 병렬로 처리한다.. 각 조각은 키-값 쌍으로 변환되며, 이 쌍들이 중간 결과로 생성된다. 예를 들어, 단어 빈도를 계산하는 경우, 입력 텍스트의 각 단어를 키로, 단어의 출현 횟수를 값으로 하는 쌍이 만들어 질 수 있다.

     

    그 다음, 리듀스(Reduce) 단계에서는 맵 단계에서 생성된 중간 결과를 취합하여 최종 결과를 생성한다. 같은 키를 가진 데이터들을 모아서 지정된 연산을 수행한다. 단어 빈도 계산의 예시에서는 같은 단어에 대한 빈도들을 합산하여 최종 빈도를 계산한다.

     


    맵 리듀스 작업 실행하기

     

    1. 입력 파일을 읽는다. 레코드로 쪼갠다. 예 ) 레코드 분리자로 '\n' 을 사용

    2. 각 입력 레코드마다 매퍼 함수를 호출해 키와 값을 추출한다.

    3. 키를 기준으로 키와 값 쌍을 모두 정렬한다.

    4. 정렬된 키 값 쌍 전체를 대상으로 reduce 함수를 호출한다. 같은 키가 여러 번 등장했다면 정렬 과정에서 해당 키-값 쌍은 서로 인접한다. 따라서 같은 키를 가지는 값들을 따로 메모리 상에 상태를 유지하지 않고도 쉽게 결합할 수 있다.

     

    2, 4 단계는 사용자가 직접 작성한 데이터 처리 코드다. 1단계는 파일을 나누어 레코드를 만드는 데 입력 형식 파서를 쓴다. 3단계는 정렬 단계로 맵리듀스에 내재하는 단계에서 직접 작성할 필요가 없는데 매퍼의 출력은 리듀스로 들어가기 전에 이미 정렬됐기 때문이다.

     

    맵리듀스 작업을 생성하려면 매퍼와 리듀서라는 두 가지 콜백 함수를 구현해야 한다.

     

    매퍼 ( 정렬에 적합한 형태로 데이터를 준비 )

    - 매퍼는 모든 입력 레코드마다 한번씩만 호출된다. 매퍼는 입력 레코드로부터 키와 값을 추출하는 작업이다. 각 입력으로부터 생성하는 키-값 쌍은 빈 쌍을 포함해 원하는 만큼 생성 가능하다. 매퍼는 입력 레코드로부터 다음 레코드까지 상태를 유지하지 않기 때문에 각 레코드를 독립적으로 처리한다.

     

    리듀서 ( 정렬된 데이터를 가공 )

    - 맵리듀스 프레임워크는 매퍼가 생산한 키-값 쌍을 받아 같은 키를 가진 레코드를 모으고 해당 값의 집합을 반복해 리듀서 함수를 호출한다. 리듀서는 출력 레코드를 생상한다.

     

     

    이 과정을 통해 대용량 데이터를 효율적으로 분산 처리하고, 병렬 처리를 통해 성능을 크게 향상시킬 수 있다. 대표적인 구현체로는 Apache Hadoop이 있다. 

     

    하둡 맵리듀스 작업의 병렬 실행은 파티셔닝을 기반으로 한다. 작업 입력으로 HDFS 상의 디렉터리를 사용하는 것이 일반적이고, 입력 디렉터리 내 각 파일 또는 파일 블록을 독립된 맵 태스크에서 처리할 독립 파티션으로 간주한다.

     

    HDFS

     

    HDFS는 대용량 데이터를 저장하기 위해 설계된 분산 파일 시스템이며, Hadoop 맵 리듀스 구현에서의 파일 시스템을 말한다. 

    주요 특징은 다음과 같다.

    1. 분산 저장: 데이터를 여러 노드에 분산하여 저장한다.. 이는 데이터의 신뢰성을 높이고, 대규모 데이터 세트를 처리할 수 있게 한다.
    2. 중복 저장: 각 데이터 조각(블록)을 여러 노드에 복제하여 저장한다. 이는 하드웨어 고장 시 데이터 손실을 방지한다
    3. 대규모 파일 지원: 매우 큰 파일을 여러 블록으로 나누어 저장하므로, 대용량 파일을 쉽게 저장하고 접근할 수 있다.

     

    HDFS는 확장성이 뛰어나다. HDFS를 이용한 데이터 저장과 접근 비용은 범용 하드웨어와 오픈소스 소프트웨어를 사용하기 때문에 동급 용량의 전용 저장소 장치를 사용하는 비용보다 훨씬 저렴하다. 그렇기에 대규모 확장이 가능하다.

     

    블록(Block)이란 무엇인가?

     

     

    • 블록은 파일 시스템에서 데이터를 나누는 기본 단위이다.
    • HDFS에서는 파일을 일정 크기의 블록으로 나눈다. 기본적으로 블록 크기는 128MB 또는 256MB로 설정될 수 있지만, 사용자가 조정할 수 있다.

     

    블록으로 나누어 저장하는 이유

    1. 확장성:
      • 큰 파일을 여러 블록으로 나누면 각 블록을 여러 노드에 분산하여 저장할 수 있다. 이는 파일 시스템이 매우 큰 파일을 효율적으로 저장하고 관리할 수 있게 한다.
      • 여러 블록으로 나누어진 파일을 여러 노드에서 동시에 읽고 쓸 수 있으므로, 처리 속도가 빨라진다.
    2. 신뢰성:
      • 각 블록은 여러 복제본(replica)을 가지며, 이 복제본들은 다른 노드에 저장된다. 이는 하나의 노드가 고장나더라도 데이터 손실을 방지할 수 있게 한다.
      • HDFS는 기본적으로 각 블록의 복제본을 세 개로 유지한다. 하나의 노드가 고장나면 다른 복제본을 사용하여 데이터를 복구할 수 있다.
    3. 병렬 처리:
      • 여러 노드에 분산 저장된 블록들을 병렬로 처리할 수 있어, 대용량 데이터 처리 작업(예: MapReduce 작업)이 더 효율적이다.
      • 여러 블록을 동시에 읽고 쓰기 때문에 대용량 데이터를 빠르게 처리할 수 있다.

    예를 들어, 1GB 크기의 파일을 HDFS에 저장한다고 가정해보자. HDFS가 사용하는 블록 크기가 128MB라면, 이 파일은 8개의 블록으로 나누어진다. 이 블록들은 HDFS 클러스터의 여러 노드에 분산 저장된다.

    1. 파일 분할:
      • 1GB 파일을 128MB 크기의 8개의 블록으로 나눈다.
      • 각 블록은 Block 1, Block 2, ..., Block 8로 나뉜다.
    2. 분산 저장:
      • Block 1은 노드 A, Block 2는 노드 B, Block 3은 노드 C 등으로 분산 저장된다.
      • 각 블록의 복제본도 다른 노드에 저장된다. (예: Block 1의 복제본은 노드 B와 노드 C에 저장).
    3. 데이터 접근 및 처리:
      • MapReduce 작업을 수행할 때, 각 맵(Map) 작업은 서로 다른 노드에서 병렬로 블록을 읽어 처리할 수 있다.
      • 이는 작업의 병렬성과 속도를 크게 향상시킨다.

     

    입력 디렉터리 내 각 파일 또는 파일 블록을 독립된 맵 태스크에서 처리할 독립 파티션으로 간주한다. 

     

    1. 맵(Map) 단계

    • HDFS 입력 디렉터리에서 데이터를 읽어들인다. 여기서 데이터는 3개의 맵 태스크(Mapper Task)로 분산된다.
    • 각 맵 태스크는 입력 데이터를 처리하여 키-값 쌍을 생성한다. 예를 들어, 맵 태스크 1은 입력 데이터 m1을 처리하고, 맵 태스크 2는 m2를, 맵 태스크 3는 m3를 처리한다.

    2. 셔플(Shuffle) 및 정렬(Sort) 단계

    • 맵 단계에서 생성된 중간 키-값 쌍들은 셔플과 정렬 과정을 거쳐 리듀스 태스크로 전달된다.
    • 이미지에서 보면 각 맵 태스크의 출력이 여러 리듀스 태스크로 분배됩니다. 예를 들어, 맵 태스크 1의 출력(m1, r1, m1, r2, m1, r3)이 리듀스 태스크 1, 2, 3으로 분배되며 이는 키 값에 따라 데이터를 적절한 리듀서로 보내는 과정이다.
    • 이 과정은 모든 맵퍼에서 생성된 동일한 키를 가진 중간 데이터를 동일한 리듀서로 보내기 위해 수행된다.

    3. 리듀스(Reduce) 단계

    • 리듀스 태스크는 셔플 및 정렬 단계에서 전달된 키-값 쌍을 받아서 처리한다. 여기서 같은 키를 가진 값들을 모아서 최종 결과를 계산한다.
    • 예를 들어, 리듀스 태스크 1은 m1, r1, m2, r1, m3, r1 데이터를 받아 처리하고, 최종 결과 r1을 생성한다.
    • 마찬가지로, 리듀스 태스크 2는 m1, r2, m2, r2, m3, r2를 처리하여 r2를 생성하고, 리듀스 태스크 3는 m1, r3, m2, r3, m3, r3를 처리하여 r3를 생성한다.

    4. 결과 저장

    • 각 리듀스 태스크의 출력 결과는 HDFS 출력 디렉터리에 저장된다. 이는 최종 처리 결과가 HDFS에 저장되는 것을 의미한다

     

     

    맵 테스크를 m1,m2,m3으로 표시했다. 각 입력 파일은 보통 크기가 수백 메가바이트에 달한다. 각 매퍼 입력 파일의 복제본이 있는 장비에 RAM과 CPU에 여유가 충분하다면 맵리듀스 스케쥴러가 입력 파일이 있는 장비에서 작업을 수행하려 한다. 이 원리를 데이터 가까이에서 연산하기 라 하는데, 이 원리를 적용하면 네트워크를 통해 입력 파일을 복사하는 부담과 네트워크 부하가 감소하고 지역성이 증가한다.

     

    리듀서 측 연산도 파티셔닝 되는데 맵 태스크 수는 입력 파일의 블록 수로 결정되지만, 리듀스 태스크 수는 사용자가 설정한다. 즉 맵 태스크 수와 리듀스 태스크 수는 다를 수 있다. 맵리듀스 프레임워크는 같은 키를 가진 모든 키-값 쌍을 같은 리듀서에서 처리하는 것을 보장하는데, 특정 키-값 쌍이 어느 리듀스 태스크에서 수행될지 결저하기 위해 키의 해시 값을 사용한다.

     

    파티셔닝 과정 설명

    파티셔닝은 맵 단계에서 생성된 키-값 쌍을 여러 파티션으로 나누는 과정이다. 이 과정을 통해 동일한 키를 가진 모든 데이터가 동일한 리듀스 태스크로 전달되도록 한다. 

    1. 맵 단계 (그림의 왼쪽 부분):
      • 각 맵 태스크는 입력 데이터를 처리하여 키-값 쌍을 생성한다.
      • 예: 맵 태스크 1(m1)이 키1-값1, 키2-값2를 생성한다.
    2. 파티셔닝:
      • 생성된 키-값 쌍은 키의 해시값을 기반으로 여러 파티션으로 나뉜다.
      • 동일한 키를 가진 데이터는 동일한 파티션에 속하게 된다.
      • 예를 들어, 키1과 키2가 서로 다른 파티션에 속할 수 있다.
    3. 로컬 정렬 및 저장:
      • 각 파티션은 맵 태스크의 로컬 디스크에 정렬된 상태로 저장된다.
      • 예: 파티션1에는 키1-값1, 파티션2에는 키2-값2가 저장된다.
    4. 셔플 및 정렬 단계 (그림의 가운데 부분):
      • 맵 태스크가 작업을 완료하면, 맵리듀스 스케줄러는 리듀스 태스크에게 이 맵 태스크의 출력 파일이 준비되었음을 알린다.
      • 리듀스 태스크는 각 맵 태스크로부터 자신이 담당할 파티션의 데이터를 가져온다.
      • 예: 리듀스 태스크 1(r1)은 모든 맵 태스크로부터 파티션1의 데이터를 가져온다.
    5. 리듀스 단계 (그림의 오른쪽 부분):
      • 리듀스 태스크는 병합된 데이터를 받아 같은 키를 가진 값들을 처리한다.
      • 예: 리듀스 태스크 1(r1)은 키1과 관련된 모든 데이터를 처리한다.

     

    인접한 데이터를 모으는 과정은 주로 두 단계에서 이루어진다: 파티셔닝(Partitioning) 단계와 셔플(Shuffle) 및 정렬(Sort) 단계이다.

    구체적인 과정

    1. 맵 단계

    • 맵 태스크는 입력 데이터를 처리하여 키-값 쌍을 생성한다.
    • 생성된 키-값 쌍은 키의 해시값을 이용해 여러 파티션으로 나뉜다.
    • 각 파티션은 맵 태스크의 로컬 디스크에 정렬된 상태로 저장된다.

    2. 셔플 및 정렬 단계

    • 맵 태스크가 작업을 완료하면, 리듀서는 각 맵 태스크로부터 자신이 담당할 파티션의 데이터를 다운로드한다.
    • 리듀서는 다운로드한 데이터를 키를 기준으로 정렬한다.
    • 이 정렬 과정에서 동일한 키를 가진 값들이 인접하게 배열된다.

    3. 리듀스 단계

    • 리듀스 태스크는 정렬된 데이터를 받아 같은 키를 가진 값들을 처리한다.
    • 같은 키를 가진 값들이 인접하게 배열되어 있으므로, 리듀스 태스크는 효율적으로 데이터를 처리할 수 있다.

    예시

    1. 맵 단계:
      • 맵 태스크 1: ('apple', 1), ('banana', 1), ('apple', 1)
      • 맵 태스크 2: ('apple', 1), ('banana', 1)
    2. 파티셔닝:
      • 파티션 1: ('apple', 1), ('apple', 1)
      • 파티션 2: ('banana', 1)
    3. 셔플 및 정렬:
      • 리듀서 1은 모든 맵 태스크로부터 파티션 1의 데이터를 받아 정렬.
      • 리듀서 2는 모든 맵 태스크로부터 파티션 2의 데이터를 받아 정렬.
      • 정렬된 데이터에서 동일한 키를 가진 값들이 인접하게 배열됨.
    4. 리듀스 단계:
      • 리듀서 1은 ('apple', [1, 1, 1])을 처리하여 최종 결과를 생성.
      • 리듀서 2는 ('banana', [1, 1])을 처리하여 최종 결과를 생성.

    전체 과정 요약

    1. 맵 단계:
      • 입력 데이터를 처리하여 키-값 쌍을 생성하고, 이를 파티셔닝하여 로컬 디스크에 저장합니다.
    2. 파티셔닝:
      • 키의 해시값을 이용해 데이터를 여러 파티션으로 나누고, 각 파티션은 로컬 디스크에 정렬된 상태로 저장됩니다.
    3. 셔플 및 정렬:
      • 맵 태스크가 완료되면, 리듀스 태스크가 각 맵 태스크로부터 파티션 데이터를 다운로드합니다.
      • 데이터는 리듀스 태스크로 전송되기 전에 정렬됩니다.
    4. 리듀스 단계:
      • 리듀스 태스크는 병합된 데이터를 받아 정렬된 상태로 처리하여 최종 결과를 생성합니다.
      • 최종 결과는 HDFS와 같은 분산 파일 시스템에 저장됩니다.

     

    요약하자면 키를 기준으로 데이터가 같은 파티션에 모이고, 각 파티션의 데이터가 정렬된 상태로 리듀스 태스크에 전달된다. 리듀스 태스크는 이러한 정렬된 데이터를 처리한다.

     

    MapReduce 작업을 여러 단계로 연결하고 전체 데이터 처리 파이프라인을 구성할 때, Workflow 관리 도구나 스케줄러 도구를 사용한다.  이러한 도구들을 사용하면 여러 MapReduce 작업을 순차적으로 또는 병렬로 실행할 수 있고, 복잡한 데이터 처리 워크플로우를 관리할 수 있다.

     


    참고:

    책: 데이터 중심 어플리케이션 설계

     

Designed by Tistory.