2. 내용
● MapReduce 이해
○ 기본 흐름
○ 하둡에서의 구조
● 간단한 샘플 코드
○ 국내 기상 데이터 이용 관측소 별 최고 온도 구하기
○ 로컬 테스트/클러스터 실행
● 리듀서 병렬 실행/콤바이너
3. MapReduce의 기본 흐름
입력파일
111_10_…
110_30_.
004_-20_…
111_40_…
004_5_...
(0, 111_10_…)
(30, 110_30_….)
(61, 004_-20_….)
(87, 111_40_….)
(103, 004_5_….)
Map 출력
Map 입력
MR 프레임워크가
입력 파일로부터
(키, 값) 쌍을
맵 함수에 전달
Map 함수
(k1, v1)
→
(k2, v2)
셔플
(111, 10)
(110, 30)
(004, -20)
(111, 40)
(004, 5)
(004, [-20, 5])
(110, [30])
(111, [10,
40])
Map 함수:
(키, 값) 쌍을 입력받아
새로운 (키, 값) 쌍을 생성
MR 프레임워크가
Map의 출력을
키를 기준으로 묶고
정렬
Reduce 출력
Reduce 함
수
(k2, list
(v2))
→
(k3, v3)
(004, -20)
(110, 30)
(111, 10)
Reduct 함수:
셔플 결과를 입력으로 받고,
(키, 값) 쌍을 생성
4. 하둡에서의 MR (하둡1 기준)
6. 데이터 위치 고려해
태스크 할당
1. Job ID 요청
잡클라이언트
2. HDFS에 잡 파일(jar 파일)
및 맵 작업 개수(스플릿) 정보
생성
(기본 10개의 복사본이 생성됨)
잡트래커
(JobTracker)
3. 잡 제출
4. 스플릿 정보 읽
어와 맵 및 리듀스
태스크 생성
8. HDFS에서 잡
파일(jar) 로컬 복
사
7.할당받은 맵
태스크 가져옴
맵 완료시 결과
통지
태스크트래커
(TaskTracker)
9. 태스크
JVM 실행
10. HDFS에서
데이터 읽어와
맵 실행
맵 결과는 로컬
파일에 보관
5. 데이터 위치 확인
9) 맵 결과
복사
태스크 JVM
네임노드
7) 할당받은 리듀스
태스크 가져옴
주기적으로 맵 출력
위치 문의
태스크트래커
(TaskTracker)
8) 태스크
JVM 실행
태스크 JVM
하둡 클러스터
10) 리듀스 실행
5. 셔플 과정
맵의 출력은 일단 메모리
버퍼에 보관
조건에 따라 파일로 내려
짐(spill)
스필 파일을 파
티션 별로 병합
(정렬 수행)
spill
매퍼
버퍼
P1
각 매퍼에서 파
일 가져옴
병합된 파일을
생성 후, 리듀서
입력으로 사용
P1
병합됨
spill
P2
P1
spill
버퍼 데이터를 파일에 보
관할 때, 파티션 별로 키
정렬 수행
(파티션: 리듀서의 개수)
다른 맵 노드에서
다른 리듀스 노드로
관련 자료: http://grepalex.com/2012/11/26/hadoop-shuffle-configurables/
리듀서
6. 매퍼와 리듀서의 개수
● 매퍼의 개수
○ 입력 파일 / 스플릿 크기
■ 기본: 스플릿 크기 = 블록 크기
● 리듀서의 개수
○ 잡 설정에서 개수 지정
○ 병렬 실행을 고려해서 개수 지정
● 파티셔너
○ 매퍼의 결과를 분리할 때 사용
○ 리듀서 개수에 맞게 매퍼 결과를 분리
■ 기본: hash(key) % 리듀스개수
7. MR 프로그래밍
● 데이터 준비
○ 로컬에 테스트 용 파일
○ HDFS에 실제 파일
● 잡 구현
○ 매퍼/리듀서 구현
○ 콤바이너 구현
● 테스트
○ 단일 JVM 테스트
○ 클러스터 테스트
8. 예제 프로그래밍: 관측소 별 최고 기온
구하기
● 데이터 형식
○
○
○
station: 관측소 번호
year/month/date: 관측일
meanTemp/maxTemp/minTemp/rainFall
■ 평균 기온, 최고 기온, 최저 기온, 강수량
■ 데이터가 없으면 null
● 데이터 양
○ 1960.01.01~2012.12.31 까지 국내 관측 데이터
■ 약 150M (Big은 아님, 예제용)
[station=108, year=1960, month=1, date=1, meanTemp=-1.6, maxTemp=2.2, minTemp=-5.2, rainfall=null]
[station=108, year=1960, month=1, date=2, meanTemp=-1.9, maxTemp=1.2, minTemp=-5.6, rainfall=0.4]
…
[station=102, year=1960, month=3, date=15, meanTemp=null, maxTemp=null, minTemp=null, rainfall=null]
…
...
[station=188, year=2012, month=12, date=31, meanTemp=2.0, maxTemp=5.5, minTemp=0.2, rainfall=1.8]
9. 매퍼 코드
package weather;
import java.io.IOException;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
public class MaxTempMapper extends Mapper<LongWritable, Text, Text, DoubleWritable> {
private Parser parser = new Parser();
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
parser.parse(value.toString());
if (parser.getMaxTemp() != null) {
context.write( new Text(parser.getStation()), // 맵의 출력 (키)
new DoubleWritable(parser.getMaxTemp()) ); // 맵의 출력 (값)
}
}
}
10. 리듀서 코드
package weather;
import java.io.IOException;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class MaxTempReducer extends Reducer<Text, DoubleWritable, Text, DoubleWritable> {
@Override
protected void reduce(Text key, Iterable<DoubleWritable> values, Context context)
throws IOException, InterruptedException {
double maxValue = -273;
for (DoubleWritable val : values) {
if (maxValue < val.get())
maxValue = val.get();
}
context.write(key, new DoubleWritable(maxValue)); // 리듀서의 결과 (키, 값)
}
}
11. 잡 실행기
public class MaxTempDriver extends Configured implements Tool {
@Override
public int run(String[] args) throws Exception {
Job job = new Job(getConf(), "Max Temperature");
job.setJarByClass(getClass());
FileInputFormat.addInputPath(job, new Path(args[0])); // 매퍼 입력 파일
FileOutputFormat.setOutputPath(job, new Path(args[1])); // 리듀서 출력 디렉토리
job.setMapperClass(MaxTempMapper.class);
job.setReducerClass(MaxTempReducer.class);
job.setOutputKeyClass(Text.class); // 리듀서 출력 키 타입
job.setOutputValueClass(DoubleWritable.class); // 리듀서 출력 값 타입
return job.waitForCompletion(true) ? 0 : 1;
}
public static void main(String[] args) throws Exception {
int exitCode = ToolRunner.run(new MaxTempDriver(), args);
System.exit(exitCode);
}
}
16. 클러스터 실행
$ unset HADOOP_CLASSPATH
$ hadoop jar target/weather-0.0.1-SNAPSHOT.jar weather.MaxTempDriver
-conf conf/hadoop-cluster.xml
hdfs://10.50.0.202:9000/madvirus/koreaweather.txt
hdfs://10.50.0.202:9000/madvirus/output
13/12/10 13:00:37 INFO input.FileInputFormat: Total input paths to process : 1
13/12/10 13:00:37 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java
classes where applicable
13/12/10 13:00:37 WARN snappy.LoadSnappy: Snappy native library not loaded
13/12/10 13:00:40 INFO mapred.JobClient: Running job: job_201312100954_0002
13/12/10 13:00:41 INFO mapred.JobClient: map 0% reduce 0%
13/12/10 13:00:49 INFO mapred.JobClient: map 66% reduce 0%
13/12/10 13:00:59 INFO mapred.JobClient: map 66% reduce 22%
13/12/10 13:01:00 INFO mapred.JobClient: map 100% reduce 22%
13/12/10 13:01:07 INFO mapred.JobClient: map 100% reduce 100%
13/12/10 13:01:07 INFO mapred.JobClient: Job complete: job_201312100954_0002
13/12/10 13:01:07 INFO mapred.JobClient: Counters: 26
17. 클러스터 실행(계속)
13/12/10 13:01:07 INFO mapred.JobClient: Job Counters
13/12/10 13:01:07 INFO mapred.JobClient: Launched reduce tasks=1
13/12/10 13:01:07 INFO mapred.JobClient: SLOTS_MILLIS_MAPS=25023
13/12/10 13:01:07 INFO mapred.JobClient: Total time spent by all reduces waiting after reserving slots (ms)=0
13/12/10 13:01:07 INFO mapred.JobClient: Total time spent by all maps waiting after reserving slots (ms)=0
13/12/10 13:01:07 INFO mapred.JobClient: Launched map tasks=3
13/12/10 13:01:07 INFO mapred.JobClient: Data-local map tasks=3
13/12/10 13:01:07 INFO mapred.JobClient: SLOTS_MILLIS_REDUCES=14655
13/12/10 13:01:07 INFO mapred.JobClient: FileSystemCounters
13/12/10 13:01:07 INFO mapred.JobClient: FILE_BYTES_READ=27462176
13/12/10 13:01:07 INFO mapred.JobClient: HDFS_BYTES_READ=183852236
13/12/10 13:01:07 INFO mapred.JobClient: FILE_BYTES_WRITTEN=43033108
13/12/10 13:01:07 INFO mapred.JobClient: HDFS_BYTES_WRITTEN=833
13/12/10 13:01:07 INFO mapred.JobClient: Map-Reduce Framework
13/12/10 13:01:07 INFO mapred.JobClient: Map input records=1814965
13/12/10 13:01:07 INFO mapred.JobClient: Reduce shuffle bytes=15338368
13/12/10 13:01:07 INFO mapred.JobClient: Spilled Records=3064358
13/12/10 13:01:07 INFO mapred.JobClient: Map output bytes=13142162
13/12/10 13:01:07 INFO mapred.JobClient: CPU time spent (ms)=21170
13/12/10 13:01:07 INFO mapred.JobClient: Total committed heap usage (bytes)=685572096
13/12/10 13:01:07 INFO mapred.JobClient: Combine input records=0
13/12/10 13:01:07 INFO mapred.JobClient: SPLIT_RAW_BYTES=342
13/12/10 13:01:07 INFO mapred.JobClient: Reduce input records=1098094
13/12/10 13:01:07 INFO mapred.JobClient: Reduce input groups=93
13/12/10 13:01:07 INFO mapred.JobClient: Combine output records=0
13/12/10 13:01:07 INFO mapred.JobClient: Physical memory (bytes) snapshot=816738304
13/12/10 13:01:07 INFO mapred.JobClient: Reduce output records=93
13/12/10 13:01:07 INFO mapred.JobClient: Virtual memory (bytes) snapshot=2920493056
13/12/10 13:01:07 INFO mapred.JobClient: Map output records=1098094
19. 리듀서 병렬 실행
● 동시에 실행할 리듀서 개수 지정
○ 주의: 작업이 작으면 쪼개는 부하가 더 큼
public class MaxTempDriver2 extends Configured implements Tool {
@Override
public int run(String[] args) throws Exception {
….
Job job = new Job(getConf(), "Max Temperature");
job.setJarByClass(getClass());
...
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(DoubleWritable.class);
job.setNumReduceTasks(3);
return job.waitForCompletion(true) ? 0 : 1;
}
public static void main(String[] args) throws Exception {
int exitCode = ToolRunner.run(new MaxTempDriver2(), args);
System.exit(exitCode);
}
}
20. 파티셔너(Partitioner)
● 매퍼의 결과를 분리할 때 사용
● 기본 파티셔너: 해시 이용
○ hash(key) % 리듀서 개수
● 데이터의 특성에 따라 커스텀 구현 가능
○ 예, 연령대 별로 파티션을 나누고 싶을 때
■ [0-10] → 1, [11-20] → 2, ...
21. 콤바이너(Combiner)
● 맵의 결과를 리듀스에 보내기 전에, 데이터를
최소화하기 위한 목적으로 사용
● 리듀스와 동일한 함수
Map 출력
Map 함수
(k1, v1)
→
(k2, v2)
Map 함수
(k1, v1)
→
(k2, v2)
맵 영역 병합
(111, 10)
(110, 30)
(004, -20)
(111, 40)
(004, 5)
(004, [-20, 5])
(110, [30])
(111, [10,
40])
(111, 20)
(005, 15)
(111, 40)
(005, 5)
(005, [5, 15])
(111, [20,40])
Combine
함수
(k2, list
(v2))
→
(k3, v3)
(004, [ 5])
(110, [30])
(111, [40])
Combine
함수
(k2, list
(v2))
→
(k3, v3)
(005, [5])
(111, [20])
리듀스 노드의
병합 결과
(004, [5])
(005, [5])
(111, [20,40])
Reduce 함
수
(k2, list
(v2))
→
(k3, v3)
22. 콤바이너 설정
public class MaxTempDriver3 extends Configured implements Tool {
@Override
public int run(String[] args) throws Exception {
...
Job job = new Job(getConf(), "Max Temperature");
job.setJarByClass(getClass());
...
job.setMapperClass(MaxTempMapper.class);
job.setReducerClass(MaxTempReducer.class);
job.setCombinerClass(MaxTempReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(DoubleWritable.class);
return job.waitForCompletion(true) ? 0 : 1;
}
public static void main(String[] args) throws Exception {
int exitCode = ToolRunner.run(new MaxTempDriver3(), args);
System.exit(exitCode);
}
}
23. 콤바이너 적용 전/후
Counters: 26
Job Counters
Launched reduce tasks=1
SLOTS_MILLIS_MAPS=25023
Total time spent by all reduces waiting after reserving slots (ms)=0
Total time spent by all maps waiting after reserving slots (ms)=0
Launched map tasks=3
Data-local map tasks=3
SLOTS_MILLIS_REDUCES=14655
FileSystemCounters
FILE_BYTES_READ=27462176
HDFS_BYTES_READ=183852236
FILE_BYTES_WRITTEN=43033108
HDFS_BYTES_WRITTEN=833
Map-Reduce Framework
Map input records=1814965
Reduce shuffle bytes=15338368
Spilled Records=3064358
Map output bytes=13142162
CPU time spent (ms)=21170
Total committed heap usage (bytes)=685572096
Combine input records=0
SPLIT_RAW_BYTES=342
Reduce input records=1098094
Reduce input groups=93
Combine output records=0
Physical memory (bytes) snapshot=816738304
Reduce output records=93
Virtual memory (bytes) snapshot=2920493056
Map output records=1098094
Counters: 26
Job Counters
Launched reduce tasks=1
SLOTS_MILLIS_MAPS=24569
Total time spent by all reduces waiting after reserving slots (ms)=0
Total time spent by all maps waiting after reserving slots (ms)=0
Launched map tasks=3
Data-local map tasks=3
SLOTS_MILLIS_REDUCES=13623
FileSystemCounters
FILE_BYTES_READ=9509
HDFS_BYTES_READ=183852236
FILE_BYTES_WRITTEN=248129
HDFS_BYTES_WRITTEN=833
Map-Reduce Framework
Map input records=1814965
Reduce shuffle bytes=5184
Spilled Records=1049
Map output bytes=13142162
CPU time spent (ms)=20330
Total committed heap usage (bytes)=685375488
Combine input records=1098094
SPLIT_RAW_BYTES=342
Reduce input records=370
Reduce input groups=93
Combine output records=370
Physical memory (bytes) snapshot=808636416
Reduce output records=93
Virtual memory (bytes) snapshot=2920337408
Map output records=1098094
24. 예제 실행 중, 트러블슈팅
●
●
Cygwin에서 MR 로컬 실행 에러 (hadoop-1.2.1 사용시)
○ 에러 메시지
■ Failed to set permissions of path: tmphadoopmadvirusmapredstagingmadvirus1210821488.staging to 0700
○ 처리 방법
■ https://github.com/congainc/patch-hadoop_7682-1.0.x-win
원격 HDFS 파일 시스템 접근 에러 관련
○ 에러 메시지
■ org.apache.hadoop.security.AccessControlException: Permission
denied: user=madvirus, access=WRITE, inode="/":ndap:
supergroup:drwxr-xr-x
○ 처리 방법
■ 권한 변경으로 처리
● $ hadoop fs -chmod 777 hdfs://bt1:9000/
25. 하둡1 MR의 제약
● 잡 트래커의 한계
○ 잡 트래커가 자원 관리/잡 관리를 모두 수행
○ 4,000 대, 동시 40,000 태스크 한계
● 자원 활용 문제
○ 노드 마다 맵/리듀스 태스크 개수 고정
■ 기본 값: 맵 태스크 2개, 리듀스 태스크 2개
■ 맵이 더 필요한 상황이어도 맵용 태스크 프로세스
를 더 할당할 수 없음
● 하둡2에서 해결하려 함
○
YARN (Yet Another Resource Negotiator)
26. 기타(앞으로 이해 필요..)
● Input/Output Format
○ File
○ Sequence
○ Map
● MR 튜닝
○ http://www.slideshare.net/gruter/mapreduce-tuning
○ 결과 압축 (매퍼/리듀서)
● YARN (하둡2)