[데이터 중심 애플리케이션 설계] 10. 일괄 처리

예니·2023년 2월 19일
0
post-thumbnail
  • 시스템을 세 가지 유형으로 구분
    • 서비스(온라인 시스템) 서비스는 클라이언트로부터 요청이나 지시가 올 때까지 기다린다. 응답 시간은 서비스 성능 측정에 중요한 지표다.
    • 일괄 처리 시스템(오프라인 시스템) 일괄 처리 시스템은 매우 큰 입력 데이터를 받아 데이터를 처리하는 작업을 수행하고 결과 데이터를 생산한다. 일괄 처리 작업의 주요 성능 지표로는 처리량이 대표적이다.
    • 스트림 처리 시스템(준실시간 시스템) 온라인과 오프라인/일괄 처리 사이의 어딘가에 있기 때문에 준실시간 처리라 불린다.
  • 일괄 처리는 신뢰할 수 있고 확장 가능하며 유지보수하기 쉬운 애플리케이션을 구축하는 데 매우 중요한 구성요소다.
  • 이번 장에서는 일괄 처리 알고리즘인 맵리듀스를 알아본다.

유닉스 도구로 일괄 처리하기

단순 로그 분석

유닉스 도구를 사용하면 수 기가바이트의 로그 파일을 수 초 내로 처리할 수 있어 강력한 도구이다.

연쇄 명령 대 맞춤형 프로그램

유닉스 연쇄 파이프를 사용하지 않고, 프로그래밍 언어로 프로그램을 작성하여 해결할 수도 있다. 이 방법이 더 읽기 쉽다.

정렬 대 인메모리 집계

작업 세트(작업에서 임의 접근이 필요한 메모리 양)를 보고 정렬과 인메모리 집계 중 방식을 선택하면 된다.

만약 허용 메모리보다 작업 세트가 크다면 정렬 접근법을 사용하는 것이 좋다.

유닉스 철학

  1. 각 프로그램이 한 가지 일만 하도록 작성하라.
  2. 모든 프로그램의 출력은 아직 알려지지 않은 다른 프로그램의 입력으로 쓰일 수 있다고 생각하라.
  3. 소프트웨어를 빠르게 써볼 수 있게 설계하고 구축하라.
  4. 프로그래밍 작업을 줄이려면 미숙한 도움보단 도구를 사용하라.

동일 인터페이스

  • 어떤 프로그램의 출력을 다른 프로그램의 입력으로 쓰고자 한다면 호환 가능한 인터페이스를 써야 한다. 특정 프로그램이 다른 어떤 프로그램과도 연결 가능하려면 프로그램 모두가 같은 입출력 인터페이스를 사용해야 한다.

로직과 연결의 분리

  • 유닉스는 표준 입력(stdin)과 표준 출력(stdout)을 사용한다. 파이프가 한 프로세스의 stdout을 다른 stdin과 연결한다. 하지만 프로그램은 입력이 어디서부터 들어오는지 출력이 어디로 나가는지 신경 쓸 필요가 없다. (느슨한 결합, 지연 바인딩, 제어 반전이라고도 함)

투명성과 실험

유닉스 도구는 진행 사항을 파악하기가 쉬워, 실험용으로 매우 좋다.

맵리듀스와 분산 파일 시스템

  • 맵리듀스는 유닉스 도구와 비슷하지만 수천 대의 장비로 분산해서 실행이 가능하다는 차이가 있다.
  • 맵리듀스 작업은 분산 파일 시스템 상의 파일을 입력과 출력으로 사용한다. 하둡 맵리듀스 구현에서 이 파일 시스템은 HDFS(Hadoop distributed file system)라고 하는데 GFS(Google file system)를 재구현한 오픈소스다.
  • HDFS는 비공유 원칙을 기반으로 하는데, NAS(Network attached storage)와 SAN(Storage area network) 아키텍처에서 사용하는 공유 디스크 방식과는 반대다. 공유 디스크 저장소는 중앙 집중 저장 장치를 사용하지만, 비공유 방식은 특별한 하드웨어가 필요없다.
  • HDFS는 각 장비에서 실행되는 데몬 프로세스로 구성된다. 데몬 프로세스는 다른 노드가 해당 장비에 저장된 파일에 접근 가능하게끔 네트워크 서비스를 제공한다. 네임노드라고 부르는 중앙 서버는 특정 파일 블록이 어떤 장비에 저장됐는지 추적한다. 따라서 HDFS는 개념적으로는 매우 큰 하나의 파일 시스템이고 데몬이 실행 중인 모든 장비의 디스크를 사용할 수 있다.
  • 파일 블록은 여러 장비에 복제된다. 단순히 여러 장비에 동일한 데이터를 복사하는 방식도 있고, 리드 솔로몬 코드 같은 삭제 코딩 방식을 사용해 데이터 전체를 복제하는 것보다 적은 저장소 부담으로 손실된 데이터를 복구하는 방식도 있다.
  • HDFS는 확장성이 뛰어나다. 그러므로 비용이 저렴해서 대규모 확장에 유리하다.

맵리듀스 작업 실행하기

  • 맵리듀스는 HDFS 같은 분산 파일 시스템 위에서 대용량 데이터셋을 처리하는 코드를 작성하는 프로그래밍 프레임워크다.
  • 맵리듀스의 데이터 처리 과정
    1. 입력 파일을 읽어, 레코드로 쪼갠다.
    2. 각 입력 레코드마다 매퍼 함수를 호출해 키, 값을 추출한다.
    3. 키를 기준으로 키-값 쌍을 모두 정렬한다.
    4. 정렬된 키-값 쌍 전체를 대상으로 리듀스 함수를 호출한다.
  • 맵리듀스 작업을 생성하려면 다음과 같이 동작하는 매퍼와 리듀서라는 두 가지 콜백 함수를 구현해야 한다.
    • 매퍼 매퍼는 모든 입력 레코드마다 한 번씩만 호출된다. 매퍼는 입력 레코드로부터 키와 값을 추출하는 작업이다.
    • 리듀서 맵리듀스 프레임워크는 매퍼가 생산한 키-값 쌍을 받아 같은 키를 가진 레코드를 모으고, 해당 값의 집합을 반복해 리듀서 함수를 호출한다. 리듀서는 출력 레코드를 생산한다.
  • 매퍼는 정렬에 적합한 형태로 데이터를 준비하는 역할을, 리듀서는 정렬된 데이터를 가공하는 역할을 한다.

맵리듀스의 분산 실행

  • 유닉스 명령어 파이프라인과의 가장 큰 차이점은 맵리듀스가 병렬로 수행하는 코드를 직접 작성하지 않고도 여러 장비에서 동시에 처리가 가능하다는 점이다.
  • 맵리듀스 프레임워크가 장비 간에 데이터가 이동하는 복잡한 부분을 처리한다.
  • 분산 연산에서 매퍼와 리듀서를 대개는 일반적인 프로그래밍 언어로 함수를 작성한다. 하둡 맵리듀스에서 매퍼와 리듀서는 각각 특정 인터페이스를 구현한 자바 클래스다.
  • 맵리듀스 작업의 병렬 실행은 파티셔닝을 기반으로 한다.
  • 각 입력 파일은 보통 크기가 크다. 그래서 각 매퍼 입력 파일의 복제본이 있는 장비에 여유가 있다면 맵리듀스 스케줄러가 입력 파일이 있는 장비에서 작업을 수행하려 한다. 이를 데이터 가까이에서 연산하기 라고 한다. 이를 적용하면 네트워크를 통해 입력 파일을 복사하는 부담과 네트워크 부하가 감소하고 지역성이 증가한다.
  • 맵 태스크 수와 리듀스 태스크 수는 다를 수 있다. 맵 태스크 수는 입력 파일의 블록 수로 결정되며, 리듀스 태스크 수는 사용자가 설정한다.
  • 각 맵 태스크는 키의 해시값을 기반으로 출력을 리듀서로 파티셔닝한다. 리듀서를 기준으로 파티셔닝하고 정렬한 뒤 매퍼로부터 데이터 파티션을 복사하는 과정을 셔플이라 한다. 리듀서를 거친 출력 레코드는 분산 파일 시스템에 파일로 기록된다.

맵리듀스 워크플로

맵리듀스 작업을 연결해 워크플로로 구성하는 방식은 꽤 일반적이다. 맵리듀스 작업 하나의 출력을 다른 맵리듀스 작업의 입력으로 사용하는 방식이다.

리듀스 사이드 조인과 그룹화

  • 여러 데이터셋에서 한 레코드가 다른 레코드와 연관이 있는 것은 일반적이다. 관계형 모델의 외래키, 문서 모델에서의 문서 참조, 그래프 모델에서의 간선
  • 데이터베이스에서 적은 수의 레코드만 관련된 질의를 실행한다면 일반적으로 색인을 사용하지만, 맵리듀스에는 색인 개념이 없다.
  • 파일 집합이 입력으로 주어진 맵리듀스 작업은 입력 파일 전체 내용을 읽는다. 적은 수의 레코드만 찾고 싶을 때는 전체 테이블 스캔이 비효율적이지만, 분석 질의는 대량의 레코드를 대상으로 집계 연산을 하기 때문에 전체 테이블 스캔이 합리적이다. 일괄 처리 맥락에서 조인은 데이터셋 내 모든 연관 관계를 다룬다는 뜻이다.

정렬 병합 조인

  • 맵리듀스에서 작업 레코드를 재배열하는 것을 보조 정렬이라 한다. (secondary sort)
  • 매퍼 출력이 키로 정렬된 후에 리듀서가 조인의 양측의 정렬된 레코드 목록을 병합하는 것을 정렬 병합 조인(sort-merge join)이라고 한다.

같은 곳으로 연관된 데이터 가져오기

  • 같은 리듀서로 연관된 데이터를 가져오면 리듀서는 단일 스레드로 동작하는 간단한 코드가 될 수 있고, 처리량이 높게 유지하면서 메모리 부담을 줄일 수 있다.
  • 매퍼가 키-값 쌍을 내보낼 때 키는 값을 보낼 목적지의 주소 역할을 한다. 같은 키를 가진 키-값 쌍은 모두 같은 리듀서를 호출한다.
  • 맵리듀스 프로그래밍 모델은 올바른 장비로 데이터를 모으는 연산의 물리적 네트워크 통신 측면과 받은 데이터를 처리하는 애플리케이션 로직을 분리한다.

그룹화

  • 조인 외에도 같은 곳으로 관련 데이터를 모으는 일반적인 사용 유형은 SQL에서 GROUP BY 절과 같이 특정 키로 레코드를 그룹화하는 것이다.
  • 맵리듀스로 그룹화 연산을 구현하는 방법은 조인의 구현과 유사하다. (매퍼가 키-값 쌍을 생성할 때 그룹화할 대상을 키로 함)

쏠림 다루기

  • 키 하나에 너무 많은 데이터가 연관된다면 같은 키를 가지는 모든 레코드를 같은 장소로 모으는 패턴은 제대로 작동하지 않는다. 이렇게 불균형한 활성 데이터베이스 레코드를 린치핀 객체 또는 핫 키라고 한다.
  • 한 리듀서가 다른 리듀서보다 엄청나게 많은 레코드를 처리해야 하는 현상을 핫스팟이라 한다.
  • 핫스팟을 완화할 몇 가지 알고리즘
    • 피그의 쏠림 조인 (skewed join) 핫 키를 여러 리듀서에 퍼뜨려서 처리하는 방법이다. 다른 조인 입력을 여러 리듀서로 복제하는 비용이 들지만 병렬화 효과가 더 크다.
    • 크런치의 공유 조인 (shared join)
    • 하이브의 맵 사이드 조인 (map-side join)

맵 사이드 조인

  • 리듀스 사이드 조인 실제 조인 로직을 리듀서에서 수행하며, 매퍼는 입력 데이터를 준비하는 역할을 한다.
    • 장점 입력 데이터에 대한 특정 가정이 필요없다.
    • 단점 정렬 후 리듀서로 복사한 뒤 리듀서 입력을 병합하는 모든 과정에 드는 비용이 상당하다.
  • 맵 사이드 조인 입력 데이터에 대해 특정 가정이 가능하다면 이 방법으로 조인을 더 빠르게 수행할 수 있다. 리듀서, 정렬 작업은 필요없다. 각 매퍼가 할 작업은 분산 파일 시스템에서 단순히 입력 파일 블럭 하나를 읽어 다시 해당 분산 파일 시스템에 출력하는 것이 전부다.

브로드캐스트 해시 조인

  • 맵 사이드 조인은 작은 데이터셋(전체를 각 매퍼 메모리에 적재 가능할 정도로 충분히 작은)과 매우 큰 데이터셋을 조인하는 경우에 적용해볼 수 있다.
  • 브로드캐스트 해시 조인 매퍼가 시작할 때 분산 파일 시스템에서 작은 데이터셋을 인메모리 해시 테이블에 넣는다. 매퍼 태스크를 여러 개 사용할 수도 있다. (모든 매퍼가 작은 입력 전체를 메모리에 적재한다) 이렇게 되면 매퍼는 큰 데이터셋을 모두 스캔하여 이를 해시 테이블에서 간단하게 조회할 수 있다.
  • 브로드캐스트는 큰 입력의 파티션 하나를 담당하는 각 매퍼는 작은 입력 전체를 읽는다는 것을 의미하고, 해시는 해시 테이블을 사용한다는 뜻이다.

파티션 해시 조인

맵 사이드 조인의 입력을 파티셔닝한다면 해시 조인 접근법을 각 파티션에 독립적으로 적용할 수 있다.

맵 사이드 병합 조인

입력 데이터셋이 같은 방식으로 파티셔닝되었고, 같은 키를 기준으로 정렬됐다면 맵 사이드 병합 조인을 적용할 수 있다.

맵 사이드 조인을 사용하는 맵리듀스 워크플로

  • 워크플로에서 맵 사이드 조인을 사용할지 리듀스 조인을 사용할지에 따라 출력 구조가 달라진다. 리듀스 사이드 조인은 조인 키로 파티셔닝하고 정렬해서 출력한다. 맵 사이드 조인은 큰 입력과 동일한 방법으로 파티셔닝하고 정렬한다.
  • 맵 사이드 조인을 수행하려면 크기, 정렬, 입력 데이터의 파티셔닝 같은 제약사항이 따른다. 조인 전략을 최적화할 때는 분산 파일 시스템 내 저장된 데이터셋의 물리적 레이아웃 파악이 중요하다.

일괄 처리 워크플로의 출력

일괄 처리는 온라인 트랜잭션 처리도 아니고 분석도 아니다.

검색 색인 구축

정해진 문서 집합을 대상으로 전문 검색이 필요하다면 일괄 처리가 색인을 구축하는 데 매우 효율적이다. 매퍼는 필요에 따라 문서 집합을 파티셔닝하고 각 리듀서가 해당 파티션에 대한 색인을 구축한다. 그리고 색인 파일은 분산 파일 시스템에 저장된다.

일괄 처리의 출력으로 키-값을 저장

일괄 처리 작업 내부에 완전히 새로운 데이터베이스를 구축해 분산 파일 시스템의 작업 출력 디렉터리에 저장하는 방법이다.

일괄 처리 출력에 관한 철학

입력을 불변으로 처리하고 외부 데이터베이스에 기록하는 등의 부수 효과를 피하므로 일괄 처리 작업은 좋은 성능을 내면서 유지보수가 간단하다.

하둡과 분산 데이터베이스의 비교

저장소의 다양성

  • 데이터베이스는 특정 모델을 따라 데이터를 구조화해야 한다. 반면 분산 파일시스템의 파일은 어떤 데이터 모델과 인코딩을 사용해서도 기록할 수 있는 연속된 바이트일 뿐이다. 하둡은 데이터가 어떤 형태라도 상관없이 HDFS로 덤프할 수 있다.
  • 이 방식은 데이터 수집의 속도가 올라간다. 데이터를 해석하는 부담을 데이터 생산자에서 데이터 해석자(소비자)로 이전시킨다.

처리 모델의 다양성

  • 하둡 위에서 SQL 질의, 맵리듀스가 아닌 다양한 일괄 처리도 직접 작성할 수 있다.
  • 하둡 접근법에서는 다른 종류의 처리를 위해 여러 다른 특정 시스템으로 데이터를 보낼 필요가 없다. 하둡 시스템은 동일한 클러스터 내에서 다양한 작업부하를 함께 지원할 수 있을 정도로 충분히 유연하다. 데이터를 옮길 필요가 없기 때문에 데이터로부터 가치를 끌어내기 쉽고 새로운 처리 모델을 사용한 실험도 훨씬 쉽다.

빈번하게 발생하는 결함을 줄이는 설계

  • 일괄 처리는 온라인 시스템에 비해 결함에 덜 민감하다. 일괄 처리 작업이 실패해도 즉시 사용자에게 영향을 주지 않으면서 언제든 다시 실행할 수 있기 때문이다.
  • 데이터베이스는 질의 실행 중에 한 장비만 죽어도 전체 질의가 중단된다. 데이터베이스는 디스크에서 읽는 비용을 피하기 위해 가능하면 메모리에 많은 데이터를 유지하려 한다.
  • 맵리듀스는 맵 또는 리듀스 태스크의 실패를 견딜 수 있다. 개별 태스크 수준에서 작업을 수행하기 때문에 전체 작업으로 보면 영향을 받지 않는다. 맵리듀스는 데이터를 되도록 디스크에 기록하려 한다.

맵리듀스를 넘어

일괄 처리 방법의 대안을 살펴보자

중간 상태 구체화

  • 모든 맵리듀스 작업은 다른 작업과 모두 독립적이다. 반드시 첫 번째 작업을 완료한 후에 두 번째 작업을 수행해야 한다. 다른 여러 작업에서 입력으로 재사용이 가능해야 한다.
  • 분산 파일 시스템 상에 있는 파일들은 단순히 한 작업에서 다른 작업으로 데이터를 옮기는 수단, 즉 중간 상태다.
  • 중간 상태를 파일로 기록하는 과정을 구체화라 한다.
  • 파이프는 중간 상태를 구체화하는 대신 작은 인메모리 버퍼만을 사용해 점진적으로 출력을 입력으로 스트리밍한다.
  • 중간 상태를 완전히 구체화하는 맵리듀스는 유닉스 파이프에 비해 여러 단점이 있다.
    • 맵리듀스 작업은 입력을 생성하는 모든 선행 작업이 완료됐을 때만 시작이 가능하다. 모든 선행 작업 태스크가 종료될 때까지 기다리면 워크플로 전체 수행 시간은 느려진다.
    • 매퍼는 종종 중복되기도 한다.
    • 분산 파일 시스템에서 중간 상태를 저장하는 것은 이 파일들이 여러 장비에 복제된다는 것인데 과잉조치다.

데이터플로 엔진

  • 위의 문제를 해결하기 위해 분산 일괄 처리 연산을 하는 엔진이 몇 가지 새롭게 개발되었다. (ex. 스파크) 이 엔진들의 공통점은 전체 워크플로를 독립된 하위 작업으로 나누지 않고 작업 하나로 다룬다는 점이다.
  • 이 엔진들은 여러 처리 단계를 통해 데이터 흐름을 명시적으로 모델링하기 때문에 이 시스템을 데이터플로 엔진이라고 부른다.
  • 맵리듀스와 달리 이 함수들은 맵과 리듀스를 번갈아 수행하는 규칙을 엄격히 지킬 필요가 없고, 더 유연한 방법으로 함수들을 조합한다. 이 함수들을 연산자라고 부르고, 연산자의 출력과 다른 연산자의 입력을 연결하는 여러 방법을 제공한다.
  • 맵리듀스 모델과 비교했을 때 장점
    • 정렬 같은 비싼 작업은 실제로 필요할 때만 수행한다.
    • 필요없는 맵 태스크는 없다.
    • 워크플로에 모든 조인과 데이터 의존 관계를 명시하므로 스케줄러가 어느 데이터가 어디 필요한지를 알아서 지역성 최적화가 가능하다.
    • 연산자 간 중간 상태는 대개 메모리나 로컬 디스크에 기록해서 IO가 적게 든다.
    • 연산자들은 입력이 준비되는 즉시 시작할 수 있다.
    • 새로운 연산자를 실행할 때 이미 존재하는 JVM을 재활용할 수 있어 시작 부담이 적다.
  • 맵리듀스 워크플로와 동일한 연산을 데이터플로 엔진을 사용해 구현할 수 있고, 최적화로 인해 속도가 훨씬 빠르다.

내결함성

  • 분산 파일 시스템에 중간 상태를 모두 구체화할 때 생기는 이점은 내구성이다.
  • HDFS에 중간 상태를 저장하지 않으면서 내결함성 확보를 위한 접근법으로는, 유효한 데이터로부터 재계산하는 방식이 있다.
  • 데이터를 재연산할 때 중요한 점은 해당 연산이 결정적인지를 파악하는 것이다. 동일한 입력 데이터에 대해 연산자들이 항상 같은 출력을 생산하는지 여부이다. 연산자를 재시작해서 재연산된 데이터가 잃어버린 원본 데이터와 같지 않다면 다운스트림 연산자가 이전 데이터와 새로운 데이터 간의 모순을 해결하기는 매우 어렵다. 아래로 전파되는 결함을 피하려면 연산자를 결정적으로 만드는 것이 중요하다.
  • 중간 데이터가 원천 데이터보다 훨씬 작거나, 연산이 CPU 중심적이라면 재연산보다 중간 데이터를 파일로 구체화하는 방식이 더 효과적이다.

그래프와 반복 처리

  • 그래프는 분산 파일 시스템에 정점과 간선 목록이 포함된 파일의 형태로 저장할 수 있다. 하지만 완료할 때까지 반복이라는 개념은 일반적인 맵리듀스로 표현할 수 없다. 맵리듀스는 데이터를 일회성으로만 처리하기 때문이다. 이런 알고리즘은 대개 반복적 스타일로 구현한다.
    • 일괄 처리 → 종료 조건 확인 → 종료되지 않았다면 다시 일괄 처리 수행

프리글 처리 모델

  • 일괄 처리 그래프를 최적화하는 방법으로 벌크 동기식 병렬(BSP) 연산 모델이 널리 사용된다. 프리글 모델로도 불린다.
  • 한 정점은 다른 정점으로 메시지를 보낼 수 있다. 이 메시지는 대개 그래프에서 간선을 따라 보내진다.
  • 맵리듀스와 프리글 모델의 차이점은 정점은 반복에서 사용한 메모리 상태를 기억하고 있다는 점이다.

내결함성

  • 프리글 모델에서 내결함성은 반복이 끝나는 시점에 모든 정점의 상태를 주기적으로 체크포인트로 저장함으로써 보장된다. 즉, 전체 상태를 지속성 있는 저장소에 기록한다.
  • 노드에 장애가 나서 인메모리 상태가 손실됐을 때 가장 간단한 해결책은 전체 그래프 연산을 마지막 체크포인트로 되돌리고 연산을 재시작하는 것이다.

병렬 실행

  • 프리글 프레임워크는 정점을 어떤 장비에서 실행할지와 올바른 장소로 메시지를 보내기 위해 네트워크 상에서 어떻게 라우팅할지를 결정하는 일을 담당한다.
  • 그래프가 단일 장비에 넣기에 너무 크다면 프리글 같은 분산 접근법을 사용해야 한다.

고수준 API와 언어

  • 직접 맵리듀스 작업을 작성하는 일은 어렵기 때문에 하이브, 스파크 등의 고수준 언어나 API가 인기를 끈다.
  • 고수준 인터페이스는 코드 작성이 간결하고 대화식 사용도 지원한다. 생산성을 높힐 수 있다.

선언형 질의 언어로 전환

  • 선언적인 방법으로 조인을 지정하면, 질의 최적화기가 최적의 수행 방법을 결정한다.
  • 고수준 API에 선언적 측면을 포함하면서 실행 중에 이용할 수 있는 질의 최적화기를 가진다면 일괄 처리 프레임워크는 MPP 데이터베이스와 비슷해진다. 동시에 확장성까지 가져서 일괄 처리 프레임워크의 장점인 유연성은 유지된다.

0개의 댓글