TIL
10장 일괄 처리
시스템의 세 가지 유형
서비스(온라인 시스템)
클라이언트의 요청이 오면 응답
서비스 성능을 측정하는 중요한 지표는 응답 시간이다.
일괄 처리 시스템 (오프라인 시스템)
매우 큰 입력 데이터를 받아 처리 후 결과 데이터를 생성
오랜 시간이 걸리기에 사용자가 작업이 끝날 때까지 대기하지 않는다.
대부분 하루에 한 번 수행과 같이 반복적 일정으로 수행한다.
주요 성능 지표로는 처리량이 대표적이다.
스트림 처리 시스템 (준실시간 시스템)
정해진 입력을 처리하는 일괄 처리와 달리 입력 이벤트가 발생한 직후 입력 데이터를 소비해 출력 데이터를 생성
일괄 처리 시스템보다 지연이 낮다.
유닉스 도구로 일괄 처리하기
단순 로그 분석
유닉스 도구를 사용해 웹서버 로그 중 가장 인기 있는 페이지 5개를 추출한다고 가정해보자.
아래 파이프라인은 로그 파일을 읽고 URL을 추출, 정렬, 중복을 제거하여 카운트한 뒤 다시 정렬해 상위 5개를 추출한다.
cat
/var/log/nginx/access.log |
awk
'{print $7}'
|
sort
|
uniq
-c
|
sort
-r
-n
|
head
-n
5
유닉스에 익숙하지 않다면 이해하기 어려운 명령어이지만 상당히 강력한 일괄 처리를 제공한다.
수 기가바이트의 로그 파일을 수 초내로 처리 가능
필요에 따라 분석 방법을 수정하기도 쉽다.
유닉스 철학
유닉스 철학의 주요 원칙은 다음과 같다.
각 프로그램이 한 가지 일만 하도록 작성한다.
모든 프로그램의 출력은 아직 알려지지 않은 다른 프로그램의 입력으로 사용할 수 있어야 한다.
소프트웨어를 빠르게 써볼 수 있게 설계하고 구축하라
프로그래밍 작업을 줄이려면 미숙한 도움보단 도구를 사용하라
bash 같은 유닉스 셸을 통해 놀랄만큼 강력한 데이터 처리 작업을 쉽게 구성할 수 있다.
유닉스 도구의 특징
동일 인터페이스
파일 디스크립터를 통해 모든 프로그램이 동일 입출력 인터페이스를 사용
로직과 연결의 분리
프로그램이 입력의 출처나 출력의 목적지를 신경 쓰지 않아도 된다.
이는 느슨한 결합, 지연 바인딩, 제어 반전의 개념을 구현한다.
투명성과 실험
진행 상황을 파악하기 쉽고 중간 결과를 확인 가능
즉 실험과 디버깅이 용이하다.
유닉스 도구의 가장 큰 제약은 단일 장비에서만 실행된다는 점이다.
이를 극복하기 위해 하둡 같은 도구가 개발되었다.
맵리듀스와 분산 파일 시스템
맵리듀스는 유닉스와 유사하지만 수천 대의 장배로 분산 실행이 가능하다.
단일 맵리듀스 작업은 하나 이상의 입력으로 하나 이상의 출력을 만들어 내는데 이는 단일 유닉스 프로세스와 유사하다.
입력을 수정하지 않고 출력만 생성하기에 부수 효과가 없다.
HDFS (Hadoop Distributed File system)
GFS(Google File System)을 재구현한 오픈소스 구현체
비공유 원칙을 기반으로 하며 각 장비에서 실행되는 데몬 프로세스로 구성된다.
데몬 프로세스는 다른 노드가 해당 장비의 파일에 접근할 수 있게 네트워크 서비스를 제공
네임노드(NameNode)라는 중앙 서버가 파일 블록의 위치를 추적
즉 HDFS는 개념적으로 매우 큰 하나의 파일 시스템이고 데몬이 실행 중인 모든 장비의 디스크를 사용할 수 있다.
파일 블록은 여러 장비에 복제되어 장애에 대비한다.
맵리듀스 작업 실행하기
맵리듀스란 HDFS 같은 분산 파일 시스템 위에서 대용량 데이터셋을 처리하는 코드를 작성하는 프로그래밍 프레임워크다.
맵리듀스 작업 실행 과정
입력 파일을 읽고 레코드로 분할
각 입력 레코드마다 매퍼 함수를 호출해 키와 값을 추출
키를 기준으로 키-값 쌍을 모두 정렬
정렬된 키-값 쌍 전체를 대상으로 리듀스 함수를 호출
맵리듀스 작업을 생성하려면 2단계(맵)과 4단계(리듀스) 작업을 위해 매퍼와 리듀서라는 두 가지 콜백 함수를 구현해야 한다.
매퍼(Mapper)
모든 입력 레코드마다 한 번씩만 호출되는 키-값을 추출하는 작업
리듀서(Reducer)
매퍼가 생성한 키-값 쌍을 받아 같은 키를 가진 레코드를 모으고 해당 값의 집합을 반복해 리듀서 함수를 호출하여 출력 레코드를 생성
맵퍼와 리듀서로 표준 유닉스 도구를 사용할 수도 있지만 대개는 일반적인 프로그래밍 언어를 사용한다.
ex) 하둡 맵리듀스에서 매퍼와 리듀서는 각각 특정 인터페이스를 구현한 자바 클래스다.
맵리듀스의 분산 실행
맵리듀스는 별도 코드 작성 없이 여러 장비에서 동시에 분산 실행이 가능하다.
작업 입력으로 HDFS의 디렉터리를 보통 사용하는데 디렉터리 내 각 파일 또는 파일 블록을 독립 파티션으로 간주한다.
각 파티션은 맵 태스크에서 매퍼 연산을 수행하고 리듀스 태스크로 전달되어 리듀서 연산을 수행한다.
같은 키를 가진 모든 키-값 쌍은 같은 리듀서에서 처리하는 것을 보장
맵 태스크 수는 입력 파일 블록 수로 결정되지만 리듀스 태스크 수는 사용자가 설정한다.
데이터셋이 매우 크기 때문에 맵리듀스는 분산 환경에서 단계를 나누어 정렬을 수행한다.
맵리듀스 워크플로
한 맵리듀스 작업의 출력을 다른 맵리듀스 작업의 입력으로 사용하는 방식
하둡 맵리듀스는 워크플로 기능을 제공하지 않아 첫 번째 맵리듀스의 출력으로 지정된 디렉터리를 다른 맵리듀스의 입력 디렉터리로 사용하는 식으로 워크플로를 구성한다.
일괄 처리 작업의 출력은 작업이 성공적이어야만 유효하기에 하둡 맵리듀스 작업 간 의존성을 관리하기 위한 다양한 스케줄러가 개발되었다.
우지(Oozie), 아즈카판(Azkaban), 에어플로(Airflow) 등
리듀스 사이드 조인과 그룹화
일괄 처리 맥락에서 조인은 데이터셋 내 모든 연관 관계를 다뤄야 한다.
적은 수의 레코드만 다룬다면 데이터베이스는 일반적으로 색인(index)를 통해 원하는 레코드를 빠르게 찾는다.
일괄 처리는 전체 데이터셋을 다루기에 전체 테이블 스캔(full table scan)이 필요하다.
분산 시스템에서의 조인을 간단하게 구현하려면 전체 테이블을 스캔하며 원격 서버에 있는 레코드를 질의하는 것인데 높은 지연과 과부하가 걸리기 쉽다.
더 좋은 방법은 필요한 데이터베이스 사본을 같은 HDFS 상에 두고 맵리듀스를 통해 효율적으로 처리하는 것이다.
리듀스 사이드 조인 - 조인 로직을 리듀서에서 수행하는 알고리즘
매퍼가 입력 레코드로부터 키-값 쌍을 정렬 상태로 추출한다.
조인할 컬럼을 키로 지정
키-값 쌍은 조인 키를 기준으로 파티셔닝 되어 각 키를 담당하는 리듀서로 전달된다.
리듀서는 보조 정렬을 수행한다.
맵리듀스에서 작업 레코드를 재배열하는 작업
ex) 특정 매퍼로부터 온 데이터를 먼저 보고 시간 순으로 정렬
리듀서는 특정 조인 키의 모든 레코드를 한 번에 처리하는데 이를 정렬 병합 조인이라 한다.
매퍼 출력이 키로 정렬된 후 리듀서가 양측 정렬된 레코드 목록을 병합
특정 키를 한 번에 처리하기에 한 번에 한 레코드만 메모리에 유지하면 되고 아무런 네트워크 요청이 필요하지 않다.
조인된 결과를 최종 출력으로 생성
키 하나에 너무 많은 레코드가 몰리는 현상을 핫스팟이라 하는데 핫스팟을 완화할 몇 가지 알고리즘이 존재한다.
쏠린 조인(skewed join)
어떤 키가 핫 키인지 샘플링 작업을 수행
핫 키를 다룬 리듀서 여러 대를 지정해 핫 키를 그 리듀서들에 퍼뜨려서 처리하게 한다.
다른 조인 입력을 여러 리듀서로 복제하는 비용이 있지만 병렬화 효과가 훨씬 크다.
맵 사이드 조인(Map-side Join)
핫 키는 테이블 메타데이터에 명시적으로 지정
핫 키와 관련된 레코드는 나머지와 별도 파일에 저장한 뒤 조인 시 맵 사이드 조인으로 처리
맵 사이드 조인은 핫 키가 아닌 레코드들을 맵 단계에서 조인하는 방식
맵 사이드 조인
맵 사이드 조인(map-side join)
입력 데이터에 대해 특정 가정(핫 키인지)이 가능한 경우 사용 가능
축소된 맵리듀스 작업으로 리듀서와 정렬 작업 없이 매퍼 단계에서 이루어진다.
매퍼 단계에서 조인이 이루어지기에 리듀서를 거치지 않고 바로 출력한다.
다양한 방식으로 구현될 수 있다.
브로드캐스트 해시 조인, 파티션 해시 조인, 맵 사이드 병합 조인
브로드캐스트 해시 조인
작은 데이터셋과 큰 데이터셋을 조인하는 경우에 적용 가능
각 매퍼 메모리에 데이터셋을 해시 테이블로 적재한 뒤 큰 데이터셋의 각 파티션에서 해시 테이블을 사용하여 조인을 수행
파티션 해시 조인
두 입력 테이블을 동일한 해시 함수로 여러 파티션으로 나누고, 각 파티션 쌍에 대해 독립적으로 해시 조인을 수행하는 방식
파티셔닝을 제대로 했다면 조인할 레코드가 모두 같은 번호의 파티션에 위치한다.
각 매퍼의 해시 테이블에 적재할 데이터 양을 줄일 수 있는 것이 장점이다.
맵 사이드 병합 조인
입력 데이터셋이 동일한 방식으로 파티셔닝되고 같은 키를 기준으로 정렬되어 있을 때 사용 가능
매퍼는 두 정렬된 데이터셋을 동시에 읽으면서 조인 키를 비교하고 일치하는 레코드를 병합한다.
일괄 처리 워크플로의 출력
일괄 처리의 목적은 일반적인 트랜잭션이나 분석과는 조금 다르다.
맵리듀스는 처음에 구글에서 검색 색인 구축을 위해 사용되었다.
정해진 문서 집합을 대상을 전문 검색이 필요하다면 일괄 처리가 색인 구축에 매우 효율적이다.
매퍼가 문서 집합을 파티셔닝하고 리듀서가 각 파티션에 대한 색인을 구축한다.
또한 머신러닝 시스템이나 추천 시스템을 구축할 수도 있다.
이러한 일괄 처리 작업의 출력은 흔히 일종의 데이터베이스가 된다.
맵리듀스 작업은 유닉스 철학을 차용하여 입력을 불변으로 처리하고 외부 데이터베이스에 기록하는데 이는 다음과 같은 장점을 가진다.
코드에 버그가 있어 출력이 잘못되었다면 코드를 이전 버전으로 돌리고 재수행하면 간단히 출력을 고칠 수 있다. (내결함성)
오류를 쉽게 되돌릴 수 있어 기능 개발을 빠르게 진행할 수 있다. (비가역성 최소화)
맵이나 리듀스 태스크 실패 시 맵리듀스 프레임워크는 해당 태스크를 자동 리스케줄링하여 동일 입력을 재실행한다.
유닉스 설계 원칙과 하둡이 잘 들어맞지만 몇 가지 다른 면도 있다.
유니스 도구 대부분은 텍스트 파일을 처리하기에 파싱에 부담이 있지만 하둡은 구조화된 파일 형식을 사용하여 파싱 부담이 덜하다.
하둡과 분산 데이터베이스의 비교
저장소의 다양성
하둡
어떤 형태의 데이터도 HDFS로 덤프할 수 있다.
대신 데이터 해셕은 소비자가 해결할 문제가 된다. (schema-on-read)
분산 데이터베이스
특정 데이터 모델(예: 관계형, 문서형)에 따라 데이터를 구조화해야 한다.
처리 모델의 다양성
하둡
아무 프로그램이나 실행할 수 있는 운영체제와 유사한 속성을 제공
다양한 처리 모델을 제공하여 유연성이 높다.
분산 데이터베이스
데이터베이스 일체식 구조로 SQL을 통해 풍부한 질의와 최적화된 질의를 할 수 있다.
하지만 SQL로 모든 종류의 처리를 하지 못하기에 한계가 존재한다.
빈번하게 발생하는 결함을 줄이는 설계
하둡
실패를 견디는 데 초점을 맞춘다.
일부 장비가 고장나도 작업을 계속할 수 있다.
중간 결과를 디스크에 저장하여 재시작에 용이
분산 데이터베이스
한 장비만 고장 나도 전체 질의가 중단된다.
일반적으로 빠르게 끝나는 질의를 처리하므로 재시도 비용이 크지 않다.
맵리듀스를 넘어
맵리듀스는 단지 분산 시스템에서 사용 가능한 프로그래밍 모델 중 하나일 뿐이지만 적합한 상황에선 매우 유용하다.
맵리듀스는 분산 시스템 상에서 단순 명료하게 추상화된 모델이다.
원시 api를 사용하는 것은 어렵지만 구수준 프로그래밍 모델을 통해 접근하면 배우기 쉽고 구현이 간단하다.
매우 견고하기에 느릴지언정 결국 성공한다.
다만 맵리듀스 실행 모델 자체에도 문제는 있다.
일부 유형에 대해 성능 저하를 유발할 수도 있음
중간 상태 구체화
중간 상태 (Intermediate state)
분산 파일 시스템 상에 있는 파일들을 한 작업에서 다른 작업으로 옮기는 수단
각 맵리듀스 작업의 출력(중간 상태)을 분산 파일 시스템의 파일로 저장한다.
이 파일들은 다음 작업의 입력으로 사용된다.
중간 상태를 파일로 기록하는 과정을 구체화(materialization)이라 한다.
유닉스 파이프를 사용해 중간 상태를 완전히 구체화하는 대신 작은 인메모리 버퍼만을 사용해 점진적으로 출력을 입력으로 스트리밍할 수 있지만 이는 단점이 존재한다.
맵리듀스는 입력을 생성하는 모든 선행 작업 완료 시에만 시작 가능하기에 부하가 한쪽으로 쏠린다면 워크플로 전체 수행 시간이 느려진다.
매퍼는 종종 중복되기도 해서 리듀서 출력을 매퍼 출력과 같은 방식으로 한다면 매퍼를 끼워넣지 않고 리듀서끼리 직접 연결할 수도 있다.
분산 파일 시스템에서 중간 상태를 저장하는 것은 중간 상태 파일이 여러 장비에 복제되었다는 의미인데 이는 과하다.
데이터플로 엔진
중간 상태 구체화의 단점을 극복하기 위한 일괄 처리 연산 엔진. ex. 스파크(Spark), 테즈(Tez) 등
전체 워크플로를 독립된 하위 작업으로 나누지 않고 작업 하나로서 다룬다.
중간 상태를 HDFS에 저장하지 않고 메모리나 로컬 디스크에 저장한다.
기존 맵리듀스와 달리 연산자(operator)를 사용해 맵과 리듀스를 무조건 번갈아 수행하는 게 아닌 더 유연한 방법으로 함수를 조합한다.
이러한 최적화로 수행 속도가 훨씬 빠르다.
내결함성
중간 상태를 구체화하지 않기에 내결함성 확보를 위해 다른 방식을 사용한다.
로컬에 입력 데이터가 남아 있으면 재연산을 수행해서 복구한다.
재연산이 가능하려면 데이터의 연산을 추적해야하는데 추적을 위해 RDD(resilient distributed dataset) 추상화를 사용한다.
재연산이 항상 정답은 아닌데 중간 데이터가 원천 데이터보다 훨씬 작거나 CPU 중심적인 연산이라면 재연산보다 중간 데이터를 구체화하는 게 더 효과적이다.
그래프와 반복 처리
일반적인 그래프 알고리즘은 정점 하나와 인접한 정점을 조인하며 특정 조건에 도달할 때까지 반복한다.
분산 파일 시스템에서 ‘완료할 때까지 반복’이라는 개념은 맵리듀스로 표현할 수 없기에 다른 방안을 사용한다.
프리글 처리 모델
일괄 처리 그래프 최적화를 위해 벌크 동기식 병렬(bulk synchronous parallel, BSP)이 널리 사용된다.
한 정점이 다른 정점으로 ‘메시지를 전달’하는데 이는 맵리듀스에서 매퍼가 리듀서에 데이터를 전달하는 것과 비슷하다.
맵리듀스와 달리 프리글은 반복해서 사용한 메모리 상태를 기억하기에 정점은 새로 들어오는 메시지만 처리하면 된다.
프리글 모델은 액터 모델과 비슷한데 각 정점을 액터로 본다면 정점 상태를 제외한 정점 사이의 메시지는 내결함성과 지속성이 있다.
내결함성
정점이 서로 메시지 전달로 통신하기에 대기 시간이 발생하지 않아 작업 성능이 향상된다.
앞선 반복에서 보낸 모든 메시지는 다음 반복에 도착됨을 보장하기에 다음 반복 시작 전에 앞선 반복은 반드시 끝나야 한다.
네트워크 문제로 메시지 유실, 중복, 지연이 발생해도프리글 구현상 다음 반복에서 메시지는 단 한 번만 처리된다.
이는 반복이 끝나는 시점에 모든 정점 상태를 주기적으로 체크포인트에 저장함으로써 보장된다.
병렬 실행
정점은 어떤 물리 장비에서 실행되는지 알 필요 없기에 메시지 전달 시 단순히 정점 ID를 사용한다.
프로그래밍 모델은 한 번에 한 개 정점을 다루기에 프리글 프레임워크가 임의적으로 그래프를 파티셔닝할 수 있다.
하지만 결과적으로 그래프 알고리즘은 장비 간 통신 오버헤드가 많이 발생하기에 크기가 단일 메모리에 넣을 수 있는 정도라면 단일 장비 알고리즘이 더 좋은 성능을 낼 수도 있다.