2. 분산 파일 시스템인 HDFS 와 분산 처리 시스템인 MapReduce로 구성
HDFS와 MapReduce는 둘다 Master/Slave 구조
HDFS
Master : Name node(메타정보관리)
Slave : Data node(실제 데이터)
MapReduce
Master : JobTracker(TaskTracker의 상태 및 전체 작업의 진행 상황등을 지속적으로 감시하며 일시
적인 장애에 대해 자동 복구 기능 제공)
Slave :TaskTracker
3.
4. • MapTask
• 레코드 단위로 처리해야 하는 작업을 담당
• ReduceTask
• 관련된 데이터 끼리 묶어서 처리해야 하는 작업을 담당
5.
6.
7. 1) 맵리듀스 잡의 입력 데이터는 논리적인 단위인 입력 스플릿으로 분리된 후, 스
플릿 별로 하나의 맵 태스크가 실행
2) 맵 태스크는 메모리 버퍼를 생성한 후, 맵의 출력 데이터를 버퍼에 기록
(기본값 : 100MB의 버퍼)
3) 버퍼가 일정한 크기에 도달하면 하둡은 해당 데이터를 로컬 디스크로 스필(spill)
참고로 스필을 진행하는 중에도 2번 단계를 계속 진행
기본 : 버퍼를 80% 사용하면 스필을 실행
하둡은 로컬 디스크에 데이터를 저장하기 전에 데이터를 분리해서 파티션을 생
성
파티션들은 맵의 출력 데이터를 사용할 리듀스 태스크와 일치하도록 생성
이때 파티션은 잡에서 설정한 파티셔너 클래스를 사용
4) 맵 태스크가 종료되기 전에 스필 파일들은 하나의 정렬된 출력 파일로 병합
기본값 : 10개의 스필 파일을 병합하도록 설정
또한 출력 파일의 파티션들은 리듀서가 HTTP 프로토콜로 접근할 수 있게 생성
스레드 기본 생성 개수 : 40개
8. 5) 리듀서는 맵 태스크가 완료되면 즉시 필요한 출력 데이터를 네트워크를 통해 복사
기본 생성 스레드 개수 : 5개
네트워크를 통해 복사된 맵 출력 데이터는 리듀스용 태스크트래커의 메모리 버 퍼로 복사(메모
리 버퍼 기본 값 : 힙 메모리의 70%)
병합 기준
-. 버퍼가 일정한 크기에 도달할 때이며, 해당 임계치 기본 : 66%
-. 메모리 버퍼에서 처리해야 할 파일 개수가 일정 개수가 돼도 병합을 실행
(파일 개수 기본값: 1000)
6) 모든 맵 출력 데이터가 복사되면 해당 데이터를 병합.
이 단계에서 키값을 기준으로 별도의 병렬 작업을 진행하지는 않음.
왜냐하면 맵에서 정렬 작업이 완료됐기 때문.
이 단계에서는 정렬을 유지하고 있는 맵 출력 데이터를 병합하기만 하면 됨.
병합은 라운드 방식으로 진행하며, 라운드 기준은 mapred-site.xml에 정의된 io.sort.fact 를 사용
ex) 30개의 맵 출력 데이터를 복사하고, io.sort.fact에는 10이 정의돼 있다고 가정.
이 경우 각 라운드는 파일 10개당 하나의 병합 파일을 만들어 최종적으로 세 개의 병합된 파일이
생성.
이때 세 개의 파일은 하나의 파일로 병합되지 않고, 즉시 리듀스 태스크의 입력 데이터로 전달됨.
7) 리듀스 메서드는 정렬된 출력 데이터에서 키별로 호출.
9. map: (K1,V1) → list(K2,V2)
combine: (K2, list(V2)) → list(K2,V2)
partition: (K2,V2) → 정수(이 정수 값은 무시됨. 오직 키에 의해 결정)
reduce: (K2, list(V2)) → list(K3,V3)
리듀스의 입력 타입은 맵의 출력 타입과 같아야 함
컴바인의 타입은 리듀스의 타입과 같은 경우가 많음
파티션은 중간 키와 값(K2,V2)에 대하여 파티션 번호를 반환하며 키에 의해서
결정됨
10. map: (K1,V1) → list(K2,V2)
combine: (K2, list(V2)) → list(K2,V2)
partition: (K2,V2) → 정수(이 정수 값은 무시됨. 오직 키에 의해 결정)
reduce: (K2, list(V2)) → list(K3,V3)
입력타입은 InputFormat클래스를 통해서 정해짐( setInputFormat() )
TextInputFormat은 기본적으로 중간타입을 LongWritable 키와Text값로 생성
( 다른 타입은 JobConf 의 메소드를 호출하여 명시적으로 설정)
K2, K3가 같다면 setMapOutputKeyClass() 호출 필요없음.
setOutputKeyClass()에 정의된 타입으로 재정의 되기 때문임.
V2,V3가 같으면 setOutputKeyClass()만 사용하면 된다.
그럼에도 중간과 최종 출력 타입을 설정하는 메소드가 모두 존재하는 이유는?
자바 제네릭때문 - 타입 삭제(Type Erasure) 기능 때문에 runtime에 타입 정보가 항상 존재하지 않음.
따라서, 명시적으로 타입정보를 하둡에 제공해야 함.
호환되지 않는 타입으로 맵리듀스 잡을 설정할 수 있음을 의미
설정된 타입은 Compile시 체크되지 않고 Job 실행 중 런타임에 발견됨
이 때문에 먼저 작은 데이터로 테스트 잡을 실행 해 보면 좋음.
((Type Erasure : JVM레벨 호환성 유지 위해 Compile시 제네릭 타입 정보를 삭제해버리는 기능 )
11. 맵 task의 결과물을 네트워크를 통해 리듀스 task로 이동시키는 과정을 최적화
하기 위한 방법 중 하나
같은 key를 가진 value들을 리스트로 묶어 새로운 key-value쌍을 만듦
즉 {key1, value1}, {key1, value2}를 {key1, list(value1, value2)}의 형태로 만드는
것
주로 연합 연산(합계, 카운팅, 최대값 등)에 사용
컴바이너를 사용하게 되면 맵 task 결과물의 사이즈를 줄일 수 있음. 즉 네트워
크의 트래픽량을 줄일 수 있게 됨.
12. Mapper
데이터를 한 줄씩 읽어서 단어 단위로 쪼갠 후 단어를 Key로 하고 단어의 개수(여기서는 무
조건 1)를Value로 하는 (key, value) 쌍을 출력
참고 소스 : http://wiki.apache.org/hadoop/WordCount
13. Recucer
Mapper로 부터 넘어온 데이터를 단어 별로 묶어서 각Value 값을 모두 합한다.
Value 값은 모두 1이기 때문에 이 값들을 합하면 각 단어의 빈도수가 계산된다.
14.
15. 하둡 프레임워크는 Java로 만들어져 있지만 MapReduce 프로그램은 반드시 자
바로 작성하지 않아도 됨
하둡 스트리밍 : 매퍼 또는 리듀서로 실행파일을 가지고 잡을 만들고 돌릴 수
있는 유틸리티
하둡 파이프 : 맵리듀스 어플리케이션 구현을 위한 c++ Api
16. • Mapper 제공 필수( 위 샘플 코드는 cat을 이용하여 읽도록 되어 있음)
• 입력포맷이TextInputFormat이면 스트리밍 출력의 키와 값은 모두Text이므로 기본
IdentityMapper를 사용하면 안되고 다른 mapper를 제공해야 함 (여기서는 cat 사용)
• 파일 내의 오프셋 주소가 키가 되고 라인의 내용이 값이 됨. 이것은 입력
데이터를 정렬하는 효과
• 스트리밍 출력키와 값 기본 설정 : 항상Text 타입
17. • 스트리밍 응용 프로그램은 구분자를 제어할 수 있으며, 이 구분자는 키/
값 쌍이 연속된 바이트로 변환되어서 표준입력을 통해 맵리듀스 프로세
스에 보내질 때 사용
Ex) 스트리밍 프로세스의 출력물이 a, b, c(구분자가 따옴표) 이고 필드 개수가 2이면
키 : a, b
값 : c
• 키와 값은 구분자로 분리
• 기본값 : t
• 구분자로 분리되는 필드들을 더하여 키로 정할 수 있음
• 입력 포맷과 출력 포맷을 구분하여 설정
18. 텍스트 파일에서 데이터베이스 까지 많은 데이터 포맷 처리 가능
텍스트
Xml
바이너리
다중입력
데이터베이스 입력
19. • 입력 스플릿 : 하나의 맵에서 처리하는 입력 파일의 청크
• 각 맵 프로세스는 단일 스플릿
• 각 스플릿은 레코드들로 나누어짐
• 맵은 각 레코드(키/값 쌍)을 차례로 처리
• 스플릿과 레코드는 논리적인 개념
• 데이터 베이스 문맥에서는 스플릿은 테이블의 일정 범위에 있는 행의 집합
• 레코드는 그 범위에서 하나의 행에 해당
20. • Java Interface인 InputSplit에 의해 표현되며, 두 개의 멤버변수를 가짐
• (스플릿은 입력 데이터를 포함하지 않는다는 것에 주의.단지 데이터에 대한 참조 객체)
• 저장소 위치 : 맵리듀스 시스템에 의해 사용
• 시스템 위치 : 태스크를 데이터가 있는 곳ㅇ 가능한 한 가장 가깝게 배치
• 크기가 큰 스플릿을 우선으로 순차적으로 처리함으로써(바이트 길이 사용) 잡 수행시간을 최
소화하도록 함
21. • 응용프로그램 작성하면서 직접 InputSplit을 직접 다룰 필요 없음 (InputFormat에 의해 생성됨)
하는일 :
- 입력 스플릿 생성
- 스플릿을 레코드로 나누는 일
스플릿 크기나 개수가 InputFormat 구현체에 의해 계산되면 클라이언트는 스플릿 정보를
JobTacker에 전송
JobTracker는 스플릿 위치 정보로TaskTracker상에서 스플릿을 처리할 MapTask 스케줄링
• TaskTracker에서 MapTask 는 스플릿의 RecordReader를 얻기 위해 getRrecordReader()로 Split
을 전달
• MapTask는 맵 함수에 전달할 Key/Value 쌍의 레코드를 생성하기 위해 RecordReader를 사용
• MapRunner: Mapper를 실행시키는 유일한 방법
22. • 원본 데이터로 파일을 사용하는 모든 InputFormat 구현체의 기본 클래스
• 제공: 하나의 잡의 입력파일을 포한하는 경로, 입력 파일의 스플릿을 생성하는 구현체
• 입력파일 경로 지정 구현 (파일 지정, 파일 패턴 지정)
• addInputPath(s)(..), setInputPaths(..)
• mapred.input.dir 속성
• 특정 입력파일 배제 필터 구현
• setInputPathFilter(..)
• 필터 미설정시 기본 필터로 시스템 숨김 파일 배제 처리
• mapred.input.path.filter.class 속성
• 입력파일 스플릿 생성 구현
• 일반적으로 HDFS 블록보타 큰 파일을 블록 크기의 스플릿으로 분할
• 스플릿 크기는 대부분 HDFS 블록의 크기와 같음
• 최소 스플릿 크기는 보통 1byte이며, 일부 포맷은 스플릿 크기에 대한 최소 크기 제한이 있음
• 스플릿 크기 계산 식
• - max( 최소크기, min(최대크기, 블록크기 ) )
• - 최소크기 < 블록크기 < 최대크기
• 스플릿 크기는 블록 크기
23. • 작은 파일: HDFS 블록보다 훨씬 더 작은 파일
• 하둡은 적은 수의 큰 파일 처리가 좋음
• (FileInputFormat에서 하나의 파일 전체나 일부분으로 스플릿 만들기 때문)
• 작은 입력 데이터가 많으면 많을수록 추가적인 bookkeeping overhead발생
• Bookkeeping overhead : 하둡에서 하나의 태스크를 할당할 때 스케줄링, 프로세스 fork, 프로세
스 간의 다수 통신, 메모리 사용 등의 비용
• 1GB이 16개의 64MB 블록으로 나누어진 경우, 10,000개의 100kb 파일로 나누어진 경우 :
• 10,000개의 파일은 각각 하나의 맵 태스크 사용
• 잡 시간은 동일한 크기의 입력 파일 1개와 16개의 맵태스크로 처리하는 것보다 10배 혹은 수백배 느림
24. • 작은 파일과 잘 동작하도록 고안된 CombineFileInputFormat을 사용하면 어느 정도
해결
1. FileInputFormat이 파일 당 스플릿을 생성했다면 CombineFileInputFormat은 파일을
같은 스플릿으로 묶음.
2. 어떤 블록을 같은 스플릿에 배치할 지는 노드와 랙의 위치를 고려함.
3. 일반적인 맵리듀스 잡에서 입력 데이터를 처리할 수 있는 속도와 비슷하게 만듦.
4.추상클래스이므로 getRecordReader() 를 구현함.
많은 수의 작은 파일은 디스크 엑세스 횟수를 증가 시키고 HDFS Name node 메모
리의 낭비를 초래.
따라서 가능하다면 너무 많은 수의 작은 파일 처리를 피하는 것이 좋음
25. • 일부 응용프로그램은 파일이 스플릿되지 않고 단일 매퍼가 각 입력 파일
전체를 저리하기를 원할 때 스플릿 방지 사용
• 기존 파일이 스플릿 된 것인지 아닌지 확인하는 방법 :
• 최소 스플릿크기를 시스템의 가장 큰 파일보다 더 크게 설정(Long.MAX_VALUE로 설정)
• FileInputFormat 하위 구현클래스의 하위 클래스를 생성하고 isSplitable()을 return false하
도록 override.
26. • 매퍼는 잡 환경 설정 객체의 속성으로부터 스플릿에 대한 정보를 획득 가
능
• 파일 스플릿 속성
• map.input.file - 처리될 입력 파일 경로
• map.input.start - 시작 지점 바이트 오프셋
• map.input.length - 바이트 길이
27. • 매퍼가 파일의 전체 내용을 액세스
• FileInputFormat 하위 클래스 구현
• isSplitable() 메소드 오버라이드하여 false 반환 (스플릿 방지)
• key → NullWritable, value → BytesWritable로 타입 결정
• RecordReader 구현
• 예제 7-3 참고(WholeFileRecordReader 구현)
• next(key, value) 메소드 내에서 연 파일을 바이트 배열에 넣고, ByteWritable 인스턴스에 바이트 배열을 할
당하도록 구현
30. • TextInputFormat : 기본 InputFormat
• 각 레코드는 입력 데이터의 라인
키 : LongWritable로서 파일 내에서 라인 시작 지점의 바이트 오프셋 주소
값 :Text : 라인의 내용(개행 혹은 캐리지 리턴 문자 제외)
FileInputFormat이 정의하는 논리적인 레코드가 HDFS 블록에 정확히 들어맞지 않는 경우, 두 블록
에 걸쳐진 레코드까지 포함하여 하나의 스플릿으로 간주함
32. • 매퍼가 고정된 개수의 입력 라인을 받고자 하는 경우(스플릿에 포함될 라인 수를 고정하는 경
우) 사용
• 라인개수는 스플릿 크기와 라인길에 좌우됨
• 키 :TextInputFormat처럼 파일에서의 바이트 오프셋
• 값 : 라인 자체
• mapred.line.input.format.linespermap 속성 - 입력 라인 수 설정 (N)
• EX) 시뮬레이션과 같이 작은 양의 입력을 취하고 많은 연산을 수행한 결과물을 내는 응용 프로
그램에 적합
• 태스크 타임아웃 발생 방지를 위해 상태나 증가된 카운터를 주기적으로 보고해야 함
33. • 연속된 레코드(XML문서 조작들)로 구성된 큰 XML문서는 문자열이나 정규표현식을 이용하여
레코드의 시작과 종료 태그를 찾아서 부분 레코드로 나눌 수 있음
• 하둡에는 StreamXmlRecordReader라고 불리는 클래스가 있음.
• 시작과 종료 태크에 대한 패턴을 알려주는 레코드 리더를 잡 설정 속성을 통해서 지정할 수 있
음
34. • 하둡의 순차 파일 포맷은 바이너리 키/값 쌍의 연속 데이터를 저장
• SequenceFileInputFormat
• Sequence 파일에서 맵리듀스의 입력으로 데이터를 사용
• 파일경로가 디렉토리인 경우 MapFile을 읽는 것으로 간주하여 MapFile을 읽어들일 수도 있음
• 키와 값은 sequence파일에 의해서 정해지며 맵 입력 유형이 일치하는지 확인해야 함
• SequenceFileAsTextInputFormat
• sequence파일의 키와 값을Text객체로 변환할 때 사용
• sequence 파일을 Streaming을 위한 입력 데이터로 만드는 경우 사용
• SequenceFileAsBinaryInputFormat
• sequence파일의 키와 값을 임의의 바이너리 객체로 변환 시키는 경우 사용
• 키와 값은 BytesWritable 객체로 캡슐화됨
35. • 서로 다른 포맷의 입력을 처리하여 동일한 맵 출력물을 생성하고자 하는 경우 사용
Ex ) 하나는 탭으로 분리된 평범한 텍스트이고 다른 하나는 바이너리 순차 파일
같은 포맷이지만 다른 외형을 가지고 있어서 다른 방식으로 해석해야 함
이런 경우, MultipleInput클래스를 사용
• 경로별로 사용할 InputFormat과 매퍼를 지정 가능
36. • RDB에서 JDBC로 데이터를 읽는 입력 포맷
• 이 포맷에 대해서 sharding기능이 없으므로 너무 많은 매퍼를 실행해서 대상 DB에 과부하를
주지 않도록 해야 함
• DBOutputFormat으로 잡의 결과물을 DB에 전송함
• MultipleInput을 이용해서 HDFS의 더 큰 데이터 세트와 조인하기 위해 작은 데이터셋을 올리
는 데 많이 사용됨
• 관계형 데이터베이스와 HDFS간 데이터를 옮기는 또 다른 방법으로 스쿱(15장에서)을 고려해
볼 수 있음.
38. • 기본 출력 포맷
• 레코드를 텍스트의 라인으로 기록
• 키와 값은 어떤 타입이라도 가능
• 키/값 쌍은 탭 문자로 분리
• 구분자는 mapred.textoutputformat.separator 속성을 이용해서 변경 가능
• NullWritable 타입을 이용해서 출력물로부터 키와 값을 억제할 수 있음(아무것도 출력하지 않
게 가능)
• toString() - 키, 값을 문자열로 변환
39. • SequenceFileOutputFormat
• 시퀀스 파일로 출력
• 쉽게 압축할 수 있기 때문에 이후 추가로 맵리듀스 잡을 수행할 때 output을 input으로 사용한다면 좋은 선
택.
• SequenceFileAsBinaryOutputFormat
• SequenceFileAsBinaryInputFormat 대응
• 원시 바이너리 포맷으로 키와 값을 SequenceFile 컨테이너에 씀.
• MapFileOutputFormat
• MapFiles을 출력으로 씀.
• 리듀서에서 키를 정렬하여 MapFile의 키를 순서대로 추가해야 함
40. • 리듀서 당 다수의 출력파일을 생성하는 경우 사용
• MultipleOutputs이 더 많은 기능을 가지며 MultipleOutputFormat은 출력 디렉토리 구조와 파일
이름을 제어할 때 유용함
• MultipleOutputFormat
• 파일과 디렉토리 이름에 대한 완벽한 제어 가능
• 하위 클래스 - MultipleTextOutputFormat,MultipleSequenceFileOutputFormat
• protectedString generateFileNameForKeyValue(key, value, name) - 하위 클래스에서 재정의하여 사용함
(출력 파일명을 제어할 수 있음)
• MultipleOutputs
• 다른 출력파일에서는 다른 타입의 키와 값 사용 가능
• 같은 잡에서 맵과 리듀스에 의해 사용
• 레코드 별 다중 출력파일 생성
• 어떤OutputFormat도 사용 가능
41. • LazyOutputFormat
• 출력할 결과가 있을 경우에만 출력파일이 생성되도록 함
• jobconf.setOutputFormatClass(..)에 지정
• 빈 파일이 생성되길 원치 않을 때 LazyOutputFormat 사용(0.21.0이상)
(FileOutputForm의 하위클래스는 내용이 없더라도 출력파일(part-nnnnn)을 생성)
DatabaseOutput
TableOutputFormat은 맵리듀스 결과물을 관계형 데이터 베이스와 HBase 테이블로 쓰기 위한
출력 포맷