etc2011. 8. 9. 10:08
- 카운터
: 카운터는 잡에 대한 통계를 수집하는 유용한 채널로서 품질 통제, 또는 응용프로그램 수준 통계를 제공한다. 카운터는 문제 진단에 유용하다.

- 내장 카운터
: 얼마나 많은 바이트들과 레코드들이 처리 되었는지에 대한 카운터들이 있어서 입력과 출력 데이터 양이 예상했던 대로인지 확인 할 수 있다. (p.310 내장카운터)
카운터는 연관있는 태스크에 의해서 관리되며, 주기적으로 태스크트래커에 보내지고 잡트래커에도 보내진다. 그래서 전역적으로 수집된다. (태스크의 카운터는 마지막 전송 시점 이후에 변경된 최신 수치만 보내는 것이 아니다. 보낼때마다 누적된 전체 수치를 보낸다. 메시지 유실로 인한 오류를 방지하기 위해서이다.)

- 사용자 정의 자바 카운터
: 코드수준에서 카운터의 집합을 정의하게 해주며 매퍼와 리듀서에서 원하는 방식으로 증가하게 할 수 있다. (p. 312 최고 기온을 구하는 응용프로그램, 기형과 없어진 필드, 품질 코드 개수를 구하는 카운터 포함)

- 동적 카운터
: Reporter 객체에서 사용하는 메소드는 그룹과 문자열 이름을 사용하는 카운터 이름을 인자로 받는다.
public void incrCounter(String group, String counter, long amount)

- 읽기 가능한 카운터 이름
: 기본적 카운터의 이름은 enum 의 완전한 자바 클래스명. 웹 UI나 콘솔에서 보여질 때 가독성이 아주 좋지는 않다. (그래서 하둡은 리소스 번들을 이용, 표시되는 이름을 변경하는 방법을 제공한다. 'Temperature$MISSING.' 대신 'Air Temperature Records' 를 볼수 있다.) 판독 가능한 이름들을 제공하는 비결은, enum 의 이름을 따서 속성파일을 생성한다. 하위 클래스들에 대해서는 구분자로 밑줄을 사용한다. (예제 8-1 의 카운터에 대한 파일명은 MaxTemperatureWithCounters_Temperature.properties, 속성파일은 ConterGroupName, MISSING.name, MALFORMED.name 등)

- 카운터 조회
: 웹 UI 와 명령행(하둡 job-counter 사용) 을 통하는 것 외에 자바 API 를 이용해서 카운터값을 조회할 수 있다.(p. 315 기온 필드 없는 레코드의 비율을 계산하는 응용프로그램)
잡이 끝났다는 것을 확인한 다음에는, RunningJob 의 getCounters() 메소드를 호출해서 하나의 잡에 있는 모든 카운터를 담은 Counters 객체를 반환한다.
enum을 인자로 취하는 getCounter()메소드를 사용해 없어진 기온필드를 갖는 레코드 숫자를 찾을수 있다.
내장된 카운터를 조회하기위해 findCounter()메소드도 있다. 그룹명(enum에 대한 완전한 자바클래스명)과 카운터명(모두 문자열)으로 해당 카운터를 참조한다.

- 사용자 정의 스트리밍 카운터
: 스트리밍 맵리듀스 프로그램은 포맷된 라인을 표준에러 스트림에 보내는 식으로 카운터를 증가시킬수 있다.
Temperature 그룹의 Missing 카운터 하나 증가시키는 방법(파이썬 코드)
sys.stderr.write("report:counter:Temperature,Missing, 1\n")


- 정렬
- 준비
: 기온을 텍스트로 객체로 저장하면 제대로 정렬할수 없다. 부호있는 정수는 사전 순서대로 정렬할 수 없기 때문이다. 대신 시퀀스 파일을 이용해서 데이터를 저장할것이다. (p. 318 기상 데이터를 SequenceFile 형태로 변환해주는 맵리듀스 프로그램)

- 부분정렬
: (p. 319 기본 HashPatitioner 를 사용하여 IntWritable 키를 가지고 SequenceFile 을 정렬하는 맵리듀스 프로그램) (정렬 순서 제어, 키에 대한 정렬 순서는 RawComparator 에 의해 제어. p. 320)

- 응용프로그램: 파티션된 MapFile 검색
: (p. 321 SequenceFile 을 정렬하고 MapFile 를 결과물로 생산하는 맵리듀스 프로그램)
(p. 322 MapFile 의 집합에서 주어진 키를 갖는 첫 번째 엔트리 가져오기)
getReaders() 메소드는 맵리듀스 잡에 의해 생성된 각각의 출력 파일에 대한 MapFile.Reader 를 오픈한다. GetEntry()메소드는 키에 대한 Reader 를 선택하기 위해 파티셔너를 사용하며 Reader 의 get() 메소드를 호출해서 키에 대한 값을 찾는다.
(p. 323 MapFile 의 집합에서 주어진 키로 모든 엔트리 가져오기)
주어진 키로 모든 레코드를 가져오기 위해 Reader 를 직접 사용할 수도 있다. (그리고나서 Reader 가 있고, MapFile 의 get() 메소드를 이용해서 첫 번째 키를 얻게 되고, 그런다음 반복적으로 next() 메소드를 호출해서 키가 바뀔때까지 다음 키와 값을 가져올것이다.)

- 전체 정렬
: 하둡을 이용해서 전체적으로 정렬된 파일을 만드는 가장 간단한 해답은 단일파티션을 이용하는것.
(서버 하나에서 모든 출력물을 처리하기 때문에 비효율적, 병렬 아키택처의 모든 장점을 포기하는것)
대신, 정렬된 파일의 집합을 만들어 낼 수 는있다. 비법은 파티셔너를 통해서 전체적으로 정렬된 순서로 만들어 내는데에 있다. 첫번째 파티션에는 기온이 -10'C 보다 작은 키들을 넣고, 두번째 파티션에는 -10'C ~ 0'C, 세번째는 0'C ~ 10'C 에 해당하는 키를 그리고 네번째는 10'C 이상의 키를 할당한다.
특정 단일 리듀서에서 처리가 늦어져서 전체 잡 시간이 지연되지 않도록 해야한다.
키를 샘플링 하는 방법을 이용하여 균등한 파티션을 만들수 있다. 전체에서 일부 키 집합을 이용해서 키 분포를 예측하고, 파티션 집합을 만드는것. (하둡이 몇개의 샘플링 예제를 제공)
(p. 326 데이터 전체 정렬을 위해 TotalOrderPartitioner 를 사용해서 IntWritable 키의 SequenceFile 을 정렬하는 맵리듀스 프로그램)
RandomSampler 를 사용 균등 확률을 갖는 키. (다목적으로 활용하기 좋다)
InputSampler, 파티션 파일을 클러스터에서 실행되고 있는 태스크와 공유하려면 분산캐시에 저장해야한다.
SplitSampler 스필릿에서 처음 n 개의 레코드를 샘플링, 정렬된 데이터에 대해서는 그다지 효과적이지 않다.(왜냐면 스플릿 전체를 대상으로 키를 선택한 것이 아니기때문)
IntervalSampler 일정한 간격으로 키를 수집하므로 정렬된 데이터에 대해서는 더 좋은 대안.
샘플링의 핵심은 균등한 크기의 파티션을 만드는것.
InputSampler 와 TotalOrderPartitioner 의 멋진 속성중 하나는 파티션 수를 마음대로 지정할수 있다는것(이것은 보통 클러스터의 리듀서 슬롯의 수에 의해 조정, 전체 개수보다 조금 작은 개수를 선택할것)

- 보조 정렬
: 맵리듀스 프레임워크는 레코드를 리듀서로 보내기 전에 먼저 키로 정렬한다. 특정키값은 정렬되지 않는다. 값이 나타나는 순서도 일정하지 않다. (이 값은 다른 맵태스크로 부터 오고 각 맵태스크는 다른시간에 실행되고 끝나기 때문) 특정방법을 이용해서 키를 정렬하고 그룹화하는 식으로 값에 순서를 부과할 수 있다. (방법 p. 329)
값으로 정렬하여 효과를 내는 방법

- 자바코드
: (p. 330 키에서 기온을 정렬해서 최고 기온을 알아내는 응용프로그램)
매퍼에서 IntPairWritable 구현 클래스사용 연도와 기온을 표현하는 키를 생성한다.

- 스트리밍
: KeyFieldBasedPartitioner 파티셔너를 사용해서 키 일부분으로 분할 할수 있다.
(연도 필드를 오름차순으로 그리고 기온을 내림차순으로 정렬하는 비교기) 리듀스 함수는 각 그룹에서 단순히 첫번째 레코드를 반환하면 되도록 할수 있다. KeyFieldBasedComparator

- 조인
: 조인을 구현하는 방법은 데이터셋이 얼마나 큰지 그리고 데이터가 어떻게 파티션 되었는지에 달렸다.

- 맵-사이드 조인
: 데이터가 맵 함수에 도달하기 전에 조인을 수행하는 식으로 동작. 각 입력 데이터셋은 동일한 개수의 파티션으로 나누어져야 하고 각 데이터 내에서 동일한 키(조인키)에 의해 정렬되어 있어야 한다.
(실행하기 위해 org.apache.hadoop.mapred.join 패키지의 CompositeInputFormat 을 사용하자.)

- 리듀스-사이드 조인
: 입력 데이터셋이 어떤 특정한 방식으로 구조화될 필요가 없다는 점에서 리듀스-사이드 조인이 더 일반적. 기본적인 아이디어는 매퍼가 각 레코드별로 태그를 붙이고 맵의 출력 키로서 조인키를 사용한다. 같은 키를 가진 레코드는 리듀서에서 함께 모이게 된다.

- 다중입력
- 보조 정렬
: TextPair 객체에 태깅한 기상청을 0, 기상레코드를 1로 기록.
(p. 339 리듀스-사이드 조인을 위해 레코드를 태깅하는 매퍼, 리듀스-사이드 조인을 위해 기상 레코드를 태깅하는 매퍼, 태그가 달린 기상청 레코드를 태그가 달린 기상 레코드와 함께 조인하는 리듀서)
(p. 340 기상청 이름으로 기상 레코드를 조인하는 응용프로그램)
잡을 묶는 드라이버 클래서. 키의 첫 번째 부분인 기상청 ID 로 분할하고 무리지으며, 자체 파티셔나와 자체 비교기인 TextPair 의 FirstComparatpor 를 사용

- 사이드 데이터 분배
: 주 데이터셋을 처리하기 위해 잡 과정에서 필요한 부가적인 읽기전용 데이터.

- 잡 환경 설정 파일 사용
: 다양한 setter 메소드를 이용하여 임의의 키 값 쌍을 잡 환경 설정에 저장할 수 있다. 태스크에서 값을 가져오기 위해서 매퍼나 리듀서에서 configure() 메소드를 오버라이드해서 전달된 JobConf 객체의 getter 메소드를 사용하면 된다.
DefaultStringifier 는 하둡의 직렬 프레임 워크를 사용해 객체를 직렬화한다.

- 분산 캐시
: 파일을 복사해서 태스크가 실행되어 파일을 사용할 시점에 파일을 분배시켜주는 서비스.

- 사용법
(p. 343~345 분산 캐시 파일로서 전달된 검색 테이블로부터 기상청 이름을 보여주면서, 기상청별 최고 기온을 찾는 응용 프로그램)

- 동작방식
: 지정한 파일을 잡트래커의 파일시스템(보통 HDFS)으로 복사. 그런다음, 태스크가 실행되기 전에 태스크트래커는 잡트래커의 파일시스템으로 파일을 로컬 디스크(혹은 캐시)로 복사한다.

- DistributedCache API
: GenericOptionsPaser 를 이용해서 간접적으로 분산캐시를 사용할수 있다.
Posted by 깜장눈썹
etc2011. 7. 26. 20:53
- 맵리듀스 잡 실행 상세분석 (p. 240 그림6-1)

- 잡 제출
  + runJob() 1: 작업실행
  : 새로운 JobClient 인스턴스를 생성하고, submitJob() 을 호출
    잡을 제출하고나서 2초에 한번씩 진행 과정을 조사하고, 변경이 생기면 콘솔에 보고한다.
  + submitJob() 메소드에 의해 구현된 잡 제출과정
   : 잡 트래커에 새로운 잡 ID 를 요청 (잡트래커 상에서 getNewJobld() 호출) (2단계)
     잡의 출력명세를 확인한다. 예를 들어 출력 디렉터리가 명시되어 있지 않거나 그것이 이미 존재한다면, 잡은 제출되지 않고 맵 리듀스 프로그램에 에러를 전달한다.
     잡에 대한 입력 스플릿들을 계산한다. 스플릿들이 계산되지 않으면, 입력 경로가 존재하지 않게 되는데, 그러면 잡이 제출되지 않고 맵리듀스 프로그램에 에러를 전달한다.
     잡 수행에 필요한 자원들인 잡 JAR 파일, 설정 파일, 그리고 계산된 입력 스플릿 정보를 잡트래커 파일시스템 상의 해당 잡 ID 를 이름으로 하는 디렉터리에 복사한다. 잡 JAR 파일은 높은 복제 수준을 가지는 요소로서(mapred.submit.replication 속성으로 설정되며, 기본설정은 10) 잡트래커들이 잡에 대한 태스크들을 생성할때 이들이 접근하는 클러스터 상에는 많은 복사본이 많들어진다.(3단계)
     잡트래커가 잡을 시작할 준비가 되었음을 알린다(잡트래커의 submitJob() 호출) (4단계)

- 잡 초기화
: submitJob() 메소드의 호출을 받으면, 잡을 내부 큐에 밀어 넣고, 잡 스케쥴러는 그것을 가져가서 초기화 한다. (초기화 과정에서 실행할 잡을 표현하기 위하여 객체 하나를 생성하는데, 이 객체는 해당 잡에 대한 태스크뿐만 아니라 각 태스크에 대한 상태 및 진행 과정을 유지하기 위한 부가적인 정보도 캡슐화 한다)
 수행할 태스크 목록을 생성하기 위해, 잡 스케쥴러는 우선 공유된 파일시스템으로부터 잡클라이언트에 의해 계산된 입력 스플릿들을 가져온다 (6단계) (그러면 각 스플릿에 대해 하나의 맵 태스크가 생성, 생성할 리듀스 태스크의 수는 JobConf의 mapred.reduce.tasks 속성을 따르는데, setNumReduceTasks() 메소드로 설정하며, 스케쥴러는 단순히 이 수만큼의 리듀스 태스크를 생성.)

- 태스크 할당
: 태스크트래커는 하트비트 메소드를 주기적으로 호출하여 잡트래커에 보내는 단순 루프를 수행.
태스크트래커는 새로운 태스크를 수행할 준비가 되었는지 여부를 표현하고, 수행할 준비가 되었다면, 잡트래커는 태스크를 할당해 주는데, 하트비트의 반환값으로 할당된 태스크를 전달한다 (7단계)

- 태스크 실행
: 잡 JAR 을 공유파일시스템으로부터 태스크트래커가 있는 지역 파일시스템으로 복사. 또한 분산캐시로부터 필요한 모든 파일들을 지역 디스크로 복사한다 (8단계)
태스크 실행을 위한 로컬 잡 디렉터리를 생성하고 JAR 을 이곳에 풀어놓는다. 태스크러너 인스턴스를 생성하여 태스크를 수행한다.
태스크러너는 JVM 을 실행 (9단계) 각잡이 이 JVM 에서 수행 (10단계)
자식 프로세스는 자신의 부모와 엄블리컬 인터페이스를통해 통신하며 태스크가 완료될때까지 태스크 진행과정을 부모에게 몇초 간격으로 알려준다.

- 스트리밍과 파이프
: 스트리밍 태스크는 표준 입출력 스트림을 통해 사용자 프로세스와 통신.
파이프태스크는 하나의 소켓을 리슨하면서 시스템환경내의 포트번호 하나를 C++ 프로세스에 전달
(태스크가 실행하는 동안 자바프로세스는 입력 키/값 쌍들을 외부 프로세스에 전달해 주고, 이 외부 프로세스는 사용자 정의 맵 또는 리듀스 함수를 통해 키/값쌍들을 처리한 후에 출력 키/값 쌍들을 자바 프로세스로 돌려준다. 태스크트래커 관점에서 보면 마치 태스크 트래커의 자식 프로세스가 스스로 맵 또는 리듀스 코드를 실행한 것 처럼 보인다.)

- 진행 상황과 상태 갱신
: 태스크가 수행되는 동안 진행 상황의 트랙이 유지, 이 트랙이 태스크의 진행률.(맵태스크에 있어서는 처리된 입력의 비율)
진행상황을 구성하는 요소 (입력 레코드 읽기, 출력 레코드 읽기, 상태 설명을 리포터에 설정하기, 카운터 증가시키기, 리포터의 progress() 메소드 호출하기)

- 잡완료
: 잡 트래커가 하나의 잡에 대한 마지막 태스크가 완료되었다는 이벤트를 통지 받았을때 그 잡에 대한 상태를 성공으로 변경한다.

- 실패
- 태스크 실패
  + 자식태스크 실패
  : 실패의 유형의 대부분은 맵 또는 리듀스 태스크 내 사용자 코드가 실행 도중에 예외를 발생할때이다. (자식 JVM 은 종료하기 전에 부모태스크 트래커에 에러를 보고. 그 에러는 결국 사용자 로그에 기록. 태스크 트래커는 태스크의 시행 상태를 실패로 표시하고 또 다른 태스크를 수행하기 위해 슬롯하나를 비운다.)
 또다른 실패유형은 자식 JVM 의 갑작스런 종료. (맵리듀스 사용자 코드에 의해 노출된 특정 상황들 때문에 JVM 이 종료되도록 하는 JVM 버그일것. 태스크 트래커는 그 프로세스가 종료되었음을 알게 되고, 시행 상태를 실패한것으로 표시)
 행걸린 태스크들은 다르게 처리된다. 태스크 트래커는 특정 태스크로부터 얼마동안 진행상황을 갱신받지 못하면 해당 태스크는 실패한것으로 표시 (태스크들이 실패한 것으로 판단하는 타임아웃 기간은 보통 10분, mapred.task.timeout 속성으로 설정가능)
잡 트래커는 태스크 트래커의 하트비트 호출을 통해 어떤 태스크의 시행이 실패했음을 통지 받으면 태스크의 실행을 재스케쥴할것이다. (잡트래커는 이전에 실패한 태스크트래커에 대해서는 태스크를 할당하지 않을것. 어떤 태스크가 네번이상 실패하게 되면 그 태스크는 더이상 시행되지 않을것이다. mapred.map(or reduce).max.attempts 속성으로 최대 시행횟수를 설정가능)
 잡의 실패가 발생하지 않는 실패 허용의 최대 비율을 설정가능(mapred.max.map(or reduce.failures.percent 속성으로 설정)
사용자들은 웹의 UI 나 명령행(옵션을 보려면 hadoop job입력) 을 사용해 태스크 시행을 강제로 종료시키거나 실패할수 있다. 잡도 강제 종료가능. (강제종료된 태스크 시행들은 태스크시행횟수로 세지 않는다. 태스크의 결함때문에 발생한것이 아니기 때문)

- 태스크 트래커 실패
: 어떤 태스크트래커가 충돌에 의해 실패하거나 매우 천천히 동작한다면, 잡트래커로 하트비트 전송을 중단할것이다. (하트비트 전송을 중단한 태스크트래커를 인식하고, 해당 태스크트래커를 태스크 스케쥴용 태스크트래커들의 풀에서 제외. 완료되지 못한 잡에 속한 맵태스크를 조사해 정상적인 태스크트래커에 배치해 재실행.)

- 잡트래커 실패
: 가장심각한 유형의 실패. 현재 하둡은 잡 트래커의 실패를 다룰수 있는 메커니즘을 갖고 있지 않다. 이런경우 전체잡이 실패.(어떤 특정서버가 실패할 가능성이 낮기 때문에 드문경우라 할수 있다?)

- 잡 스케쥴링
: mapred.job.priority 속성 또는 잡클라이언트의 setJobPriority() 메소드를 통해 잡의 우선순위 설정기능 추가 (VERY_HIGH, HIGH, NORMAL, LOW, VERY_LOW)

- 페어 스케쥴러
: 모든 사용자가 클러스터를 시간적으로 공평하게 공유할수 있도록 하는데 목적이 있다. 페어 스케줄러는 선점을 허용. 어떤 풀이 특정 기간에 공평하게 클러스터 자원을 공유하지 못했다면, 수용량 이하로 자원을 사용중인 풀에 슬롯을 제공하기 위해, 수용량 이상으로 사용중인 풀들의 태스크를 강제로 종료시킬것.
(페어 스케줄러는 'contrib' 모듈. 하둡의 contrib/fairschedule 디렉터리로부터 lib 디렉터리로 복사하면된다. org.apache.hadoop.mapred.FairScheduler 설정)

- 커패시티 스케줄러
: 클러스터는 다수의 큐로 구성되어 계층화할수 있고(따라서 하나의 큐는 다른큐의 자식일수 있다) 각큐는 할당된 수용량이 있다. 각 큐에서 잡을 FIFO 스케쥴링하는것을 제외하면 페어 스케쥴러와 같다.

- 셔플과 정렬
: 셔플은 코드기반의 영역으로서, 정제와 개선이 끊임없이 이뤄지는곳. 셔플은 맵리듀스의 심장부.
- 맵 과정
: 맵함수는 추력을 단순히 디스크에 쓰지 않는다. 일시적으로 메모리에다가 일정크기만큼 쓰기 잡을 한다음 사전정렬.
맵리듀스에서 셔플과 정렬(p. 252 그림 6-4)
버퍼의 내용이 특정한계크기에 도달하면, 백그라운드 스레드는 그내용을 디스크로 스필하기 시작.
(이렇게 스필하는 동안에도 맵 출력들은 계속해서 버퍼에 쓸것. 버퍼가 가득차게 되면 맵은 디스크로 보내는 잡이 완료될때가지 블록되어 있을것)
디스크로 쓰기에 앞서 스레드가 데이터를 나누어 파티션들을 생성하는데, 이 파티션들은 상응하는 리듀서들에 보내질것. 각 파티션 내의 배경 스레드는 메모리 내에서 키에 따라 정렬을 수행하는데, 만약 컴바이너 함수가 있다면 정렬된 출력을 바탕으로 잡을 수행.
매시간 메모리 버퍼가 스필 한계에 도달하면 새로운 스필 파일이 생성. 태스크가 끝나기 전에 스필 파일들은 하나의 출력 파일로 병합되고 정렬된다.
(컴바이너는 최종 결과에는 영향을 주지 않고 맵 출력 데이터에 대해서만 반복적으로 연산을 수행한다는 사실을 상기하자. 중요한 점은 컴바이너를 수행하면 좀더 조밀한 맵 출력을 만들게 되어, 디스크에 쓰고 리듀서에 전송해야 할 데이터가 더 적어진다는것)

- 리듀스 관점
: 맵의 출력파일은 맵태스크를 수행한 태스크트래커의 로컬 디스크 상에 놓이는데, 이제는 리듀스 태스크를 실행시킬 태스크트래커에서 해당 파티션을 필요로 한다. (리듀스 태스크는 클러스터에 걸친 몇 개의 맵 태스크들로 부터 특정 파티션에 대한 맵 출력을 필요로 한다.) 그 맵태스크들은 각기 다른시간에 마치기 때문에 리듀스 태스크는 각각이 끝나는 즉시 그 출력들을 복사하기 시작한다. 리듀스 태스크는 적은수의 카피어 스레드들을 가지는데 이들은 맵 출력들을 병렬로 인출하게 된다.
메모리의 버퍼가 한계 크기에 도달하거나, 가져올 맵 출력들의 수가 한계에 도달하면 병합되어 디스크로 옮겨진다. 복사된 파일들이 디스크에 축적되면, 배경 프로세스가 이들을 더 크고 정렬된 파일들로 병합.(이것은 나중에 있을 병합 시간을 조금이나마 줄여준다. 맵 태스크에 의해 압축된 모든 맵 출력들을 병합하기 위해 메모리에 압축을 풀어야 한다)
모든 맵 출력이 복사되면, 리듀스 태스크는 정렬 단계로 이동하게 되는데, 여기서는 맵 출력들을 병합하여 정렬순서를 유지하도록 한다. 이 잡은 라운드들의 개수 내에서 이루어진다. 각 라운드는 10개의 파일들을 하나로 병합할 것이고, 그 결과 다섯개의 중간 파일이 생성. 이 다섯개의 파일들을 하나의 정렬된 파일로 병합하는 최종 라운드를 거치지 않고, 마지막 단계(리듀스 단계)에 있는 리듀스 함수에 곧바로 전송해 디스크 IO 잡을 줄인다.(이 최종병합과정은 메모리와 디스크 상의 세그먼트들이 혼합되어 이뤄진다)

- 환경 설정 튜닝
: 셔플 튜닝(요약 사항이 기본값들을 포함해 p. 256~257 표 6-1,2)
일반적인 원칙은 셔풀에 가능한 한 많은 메모리를 할당. 맵과 리듀스 함수들이 동작하는 데 있어서 충분한 메모리를 확보하도록 보장해주어야 한다. (맵 측면에서 보면, 적은 수의 파일이 디스크로 스필될때 가장좋은 성능. 리듀스 측면에서, 중간 데이터 전체가 메모리에 있을때.)


- 태스크 실행
- 투기적 실행
: 맵리듀스 모델은 태스크들을 순차적으로 실행하는 것보다 전체적인 잡 실행 시간이 더 짧아지도록 하기위해 잡들을 태스크들로 나누고 태스크들을 병령적으로 수행.(잡실행을 느리게 만드는 태스크에 민감해질 필요가 있다.)
예상했던것보다 태스크 수행이 더 느린경우를 감지해 또 다른 동일한 예비 태스크를 실행하는것을 투기적 실행이라 한다. 두개의 복제 태스크들을 동시에 실행하여 서로를 경합시키려 하는것이 아니라는것을 이해해야한다. (투기적 태스크는 오직 해당잡에 대한 모든 태스크가 실행되고 난 후 에 실행.태스크들의 평균 진행속도보다 느린 태스크들만 해당)

- 태스크 JVM 재사용
: 하둡은 다른 실행 중인 태스크들과 불리되도록 자신의 자바 가상머신에서 태스크들을 수행. 매우짧은 시간수행을 하는 태스크들이 많거나 매우 긴 초기화 시간이걸리는 잡들이라면, 이후의 태스크들에 대해서 JVM 을 재사용하여 성능 이득을 얻을수 있다.

- 비정상 레코드 생략하기
: 에러가 있는 레코드들을 다루는 가장 좋은 방법은 매퍼 또는 리듀서 코드안에 있다. 비정상 레코드를 탐지하여 그것을 무시하게 할수있고, 또는 예외를 발생시켜 그 잡을 취소할 수 있다.
(생략모드는 태스크 시행당 오직하나의 비정상 레코드만 탐지할수 있음을 유념. 그렇기 때문에 이러한 매커니즘은 가끔 비정상 레코드들을 탐지할 목적으로만 사용하는것이 적절)

- 태스크 실행 환경
: Mapper 또는 Reducer 를 위한 configure() 메소드 구현을 통해 하나의 인자로 얻어진 잡의 환경설정 파일로부터 활용될수 있다. (p. 262 표 6-5)

- 태스크의 부작용으로 인한 파일
: 맵과 리듀스 태스크의 출력을 쓰는 일반적인 방법은 키/값 쌍들을 수집해주는 OutputCollector 를 사용하는것. 몇몇 응용프로그램들은 단일 키/값 쌍 모델보다 좀 더 많은 유연성이 필요하기 때문에, 맵 또는 리듀스 태스크의 출력 파일들을 HDFS 와 같은 분산 파일시스템에 곧바로 쓴다.
(주의해야할것은 동일한 태스크의 다중 인스턴스들이 동일한 파일에 쓰지 않도록 하는것.태스크가 실패해서 재시도가 이뤄질 경우, 그 두번째 태스크가 수행될 때 이전의 부분 출력이 여전히 남아 있을수 있다는것.  그파일에 대한 삭제가 우선 이뤄져야 한다. 또 투기적 실행이 활성화 되었을 때 같은 태스크의 두 인스턴스가 동시에 같은 파일에 쓰려고 한다는점)
하둡은 태스크 시행에 대해 독립적인 임시 디렉터리에 출력들을 쓰도록 함으로써 태스크 출력들의 충돌문제를 해결. 하둡은 응용프로그램 작성자들이 이러한 특성을 이요할수 있도록 몇몇 매커니즘을 제공.(잡 디렉터리 알아내기 등)
Posted by 깜장눈썹
etc2011. 7. 7. 20:27

- 데이터 무결성
: 일반적으로 손상된 데이터를 검출하기 위해서는 데이터가 시스템에 처음 유입되었을 때와 데이터를 손상시킬지도 모르는 신뢰할 수 없는 통신 채널로 데이터를 전송할 때 데이터에 대한 체크섬을 매번 계산하는 방법이 있다. 만일 새롭게 생성된 체크섬이 원본과 정확히 일치하지 않는다면 그데이터는 손상된것으로 간주한다. 주목할 것은, 데이터가 아니라 체크섬이 손상 될 수도 있다는것이다. 그러나, 체크섬은 데이터보다도 훨씬 작기 때문에 그럴 가능성은 매우 낮다.
일반적으로 에러검출코드는 모든 크기의 입력에 대해 32비트 정수 체크섬을 계산하는 CRC-32(순환 중복검사, cyclic redundancy check) 를 사용한다.

- HDFS 와 데이터 무결성
: io.bytes.per.checksum 에서 설정된 바이트마다 데이터에 대한 별도의 체크섬이 생성된다.
기본적으로는 512바이트이고, CRC-32 체크섬이 4바이트 long 타입이기 때문에 스토리지 오버헤드는 1%보다 적다.
데이터를 쓰고 있는 클라이언트가 데이터노드의 파이프라인으로 그 데이터를 보내고, 그 파이프라인의 마지막 데이터노드는 해당 데이터의 체크섬을 검증한다. 만약 에러가 검출되면, IOException 의 서브클래스인 ChecksumException 을 받는다.
클라이언트가 데이터노드로부터 데이터를 읽을 때, 클라이언트 역시 데이터노드에 저장된 체크섬과 수신된 데이터로부터 계산된 체크섬을 검증한다. 클라이언트 읽기과정에서 블록을 검증하는것 외에도 각 데이터노드는 저장된 모든 블록을 주기적으로 검증하기 위하여 백그라운드 스레드에서 DataBlockScanner 를 수행(이것은 물리적 저장매체에서 발생할 수도 있는 '비트로트'에 의한 데이터 손실을 피하기 위한 방법)
HDFS 는 블록의 복제본을 저장하기 때문에, 손상되지 않은 새로운 복제본 생성을 위해 정상 복제본 중 하나를 복사하는 방식으로 손상된 블록을 치료할수 있다. (파일읽기를 위한 open() 메소드를 호출하기전에 FileSystem 의 setVeriftyChecksum() 를 false로 지정함으로써 체크섬 검증을 하지 않을수도 있다.)

- LocalFileSystem
: 하둡 LocalFileSystem 은 클라이언트 측 체크섬을 수행한다. 체크섬은 파일이 읽혀질 때 검증되고, 에러가 검출되면 LocalFileSystem 이 ChecksumException 을 발생시킨다. (체크섬은 일반적으로 파일을 읽고/쓰는 시간에 몇 퍼센트의 오버헤드를 추가하는 정도, 전체 계산 성능에 미치는 영향이 미미하다)
기존 파일시스템이 원시적으로 체크섬을 지원하는 경우엔 LocalFileSystem 의 체크섬을 불능화할 수도 있다. RawLocalFileSystem 을 사용함으로써 수행할수 있다.

- ChecksumFileSystem
: LocalFileSystem 은 내부적으로 ChecksumFileSystem 을 사용하는데, 이것은 단순한 FileSystem 의 래퍼로 구현.(기존 파일시스템은 raw 파일시스템으로 표현하고, ChecksumFileSystem 의 getRawFileSystem() 메소드를 호출.)

- 압축
: 파일 압축은 파일 저장 공간을 감소시키고, 네트워크로 또는 디스크로(부터) 데이터 전송을 고속화 하는 두가지 중요한 이점이 있다.
압축포맷의 요약(p. 123). 모든 도구는 9개의 다른옵션 (-1 은 스피드 최적화, -9는 공간 최적화를 의미) 을 제공. (gzip -1 file)

- 코덱
: 압축-해제 알고리즘의 구현이다. 코덱은 CompressionCodec 인터페이스의 구현으로 표현된다.

- CompressionCodec 으로 스트림 압축 및 해제하기
: 출력 스트림에 쓰이는 데이터를 압축하기 위해, 기존 스트림에 비압축 데이터를 압축 형식으로 쓸수 있는 CompressionOutputStream 을 생성하기 위하여 createOutputStream(OptputStream out) 메소드를 사용한다. 역으로, 입력 스트림으로부터 읽히는 데이터를 압축해제하기 위하여, 기존 스트림으로부터 비압축 데이터를 읽을 수 있는 CompressionInputStream 을 얻기 위해 createInputStream(InputStream in) 을 호출한다. (코덱의 새로운 인스턴스를 생성하기 위하여 ReflectionUtils 을 사용하고, System.out 의 압축래퍼를 얻는다. 그리고 입력을 출력으로 복사하기 위해 IOUtils 의 copyBytes() 유틸리티 메소드를 호출하는데, 이때 CompressionOutputStream 에 의해 압축이 이루어진다. 마지막으로 CompressionOutputStream 의 finish() 를 호출하고, 압축도구에 압축된 스트림 쓰기 중단을 요청하지만 스트림을 닫지는 않는다.)

- CompressionCodecFactory 로 CompressionCodecs 추론하기
: 하나의 압축된 파일을 읽는 중이라면, 일반적으로 그것의 파일 확장명을 살펴봄으로써 사용중인 코덱을 추론할수 있다. (.gz 으로 끝나는 파일은 GzipCodec 으로 읽을수 있는식.)
CompressionCodecFactory 는 io.compression.codecs 환경속성에서 정의된 리스트로부터 코덱을 찾는다.

- 원시 라이브러리
: 성능 관점에서, 압축과 해제를 위해 원시 라이브러리 사용을 선호한다. 하둡은 lib/native 디렉터리에서 미리 빌드 된 32/64비트 리눅스용 원시 압축 라이브러리를 제공한다. (다른 플랫폼을 위해서는 하둡위키의 지시사항에 따라 라이브러리를 컴파일할 필요가 있다.)

- CodecPool
: 만일 원시 라이브러리를 사용 중이고 응용프로그램에서 다수 압축 또는 해제를 수행중이라면, 압축도구와 해제도구를 재사용하여 객체 생성 비용을 부분상환하는 CodecPool 사용을 고려하라.
(주어진 CompressionCodec 은 풀에서 Compressor 인스턴스로 검색되며, 그 코덱의 오버로드된 createOutputStream() 메소드에서 검색된 인스턴스를 사용. finally 를 사용하여 스트림 간 바이트를 복사하는 과정에서 IOException 이 발생할지라도 검색된 압축도구 인스턴스가 풀로 반환되도록 보장.)

- 압축과 입력 분할
- 로그파일과 같은 크고 무한한 파일을 위한 선택사항은 다음과 같다.

  • 파일을 비압축으로 저장하라.
  • bzip2 같은 분할을 지원하는 압축 포맷을 사용하라.
  • 응용프로그램에서 파일 청크로 분할하고 지원되는 모든 압축 포맷(분할에 관계없이)을 사용하여 각 청크를 개별적으로 압축하라. 이경우 압축된 청크들이 거의 HDFS 블록 하나의 크기가 되도록 청크 크기를 선택해야 한다.
  • 압축과 분항을 지원하는 시쿼스 파일을 사용하라.
  • 시퀀스파일과 같이 압축과 분할을 지원하거나 자바뿐만 아닌 다양한 언어로 읽고 쓸 수 있는 유용한 기능이 추가된 에이브로 데이터파일을 사용하라.

(큰 파일은 지역성을 보장하지 않는 맵리듀스 응요프로그램에서는 상당히 비효율적. 전체 파일에 대해 분할을 지원하지 않는 압축 포맷은 사용하지 않아야 한다.)

- 맵리듀스에서 압축 사용하기
: (입력파일이 압축되면, 맵리듀스는 파일 확장명을 통해 사용할 코덱을 결정, 파일을 읽을때 자동으로 압축을 해제함)
맵리듀스 잡의 출력을 압축하기 위해, 잡 환경 설정에서 mapred.output.compress 속성을 true 로 설정하고, 사용하고자 하는 압축 코덱의 클래스 이름으로 mapred.output.compression.codec 속성을 설정해야한다. (p. 132)

- 맵 출려을 압축하기
: 맵리듀스 응용프로그램이 비압축된 데이터를 읽고 쓰는 경우라도, 맵 상에서 중간 출력을 압축하는 것이 유리하다. (맵 출력이 디스크에 쓰이고 네트워크를 통해 리듀서 노드로 전송되기 때문에 LZO 같은 빠른 압축기를 사용함으로써 전송할 데이터 양을 줄이면 쉽게 성능을 향상시킬 수 있다.)

- 직렬화
: 직렬화는 네트워크 전송을 위해 구조화된 객체를 바이트 스트림으로 전환하는 과정. 역직렬화는 바이트 스트림을 일견의 구조화된 객체로 역전환하는 과정이다.
하둡 시스템에서 노드 사이의 프로세스 간 통신은 원격프로시져호출(RPC) 을 사용하여 구현. (RPC 프로토콜은 원격 노드로 보내기 위한 메시지를 하나의 바이너리 스트림으로 구성하기 위해 직렬화를 사용, 그 이후 원격 노드에서 그 바이너리 스트림은 원본 메시지로 재구성되기 위해 역직렬화를 사용한다.)
RPC 직렬화 포맷이 유익한 이유 (간결성, 고속화, 확장성, 상호운용성. p. 134)
하둡은 Writables 라는 자체적인 직렬화 포맷을 사용.

- Writable 인터페이스
: Writable 인터페이스는 DataOutput 바이너리 스트림으로 쓰기 위한 write() 메소드와 DataInput 바이너리 스트림으로부터 읽기 위한 readFields() 메소드를 정의.
정수는 4바이트를 사용. 그 바이트들은 빅엔디안 순서로 저장, Hadoop 의 StringUtils 의 메소드를 사용하여 16진수로 표현될수 있다.

- WritableComparable 과 컴퍼레이터
: IntWritable 은 Writable 과 java.lang.Comparable 인터페이스의 서브인터페이스에 해당하는 WritableComparable 인터페이스를 구현한다.
WritableComparator 는 WritableComparable 클래스를 위한 RawComparator 의 일반적인 목적의 구현이다.

  • WritableComparator 는 스트림으로 비교할 객체를 역질렬화하고, 그 객체의 compare() 메소드를 호출해 주는 원시 compare() 메소드의 기본적인 구현을 제공.
  • WritableComparator 는 RawComparator 인스턴스(Writable 구현들이 등록된)를 위한 팩토리 처럼 동작.


- Writable 클래스
- 자바 프리미티브를 위한 Writable 래퍼
: 자바 프리미티브를 위한 Writable 래퍼 클래스 (p. 139)
(고정길이 인코딩은 값들이 전체 값의 영역에서 잘 설계된 해시 함수처럼 매우 균일하게 분포되어 있을때 좋다. 대부분 숫자 값들은 균일하지 않은 분포를 하는 경향이 있기 때문에 평균적으로 가변길이 인코딩이 저장공간 절약 면에서 좋다.)

- Text
: Text 는 UTF-8 시퀀스들을 위한 Writable 한 구현. (Text 클래스에 대한 참조는 인코딩된 바이트 시퀀스에서의 위치 관점이지 문자열에서의 유니코드 문자나 자바 char 코드단위에서의 위치 관점은 아니다.)
- 반복하기 (p.143)
: Text 객체를 java.nio.ByteBuffer 로 변환하고, 반복해서 Text 의 bytesToCodePoint() 전역 메소드를 버퍼와 함께 호출하는 방식. (이 메소드는 다음 코드 포인트를 int 로 추출하고, 버퍼의 위치를 업데이트. 그 문자열의 끝은 bytesToCodePoint() 가 -1 을 반환할 때 인식)

- BytesWritable
: 바이너리 데이터에 대한 하나의 래퍼. 직렬화된 포맷은 뒤따르는 데이터의 바이트 길이를 지정하는 정수 필드(4바이트)와 해당 데이터가 뒤따르는 구조.
예를들어 3과 5값을 가지는 길이가 2인 바이트 배열은 4바이트 정수(00000002)와 뒤따르는 2바이트 배열(03과 05)로 직렬화된다. (000000020305)
ByteWritable 은 가변적이고, 그 값은 set() 메소드를 호출하여 변경될 수 있다. Text에서 처럼, BytesWritable 을 위해 getBytes() 메소드에서 반환된 바이트 배열의 크기(용량)는 BytesWritable 에 저장된 실제데이터 크기를 반영하지 않을 수도 있다.

- NullWritable
: NullWritable 은 Writable 의 한 특별한 타입으로 길이가 0인 직렬화를 가진다. 이 스트림으로부터는 읽거나 쓸 바이트가 없다. 이것은 자리표시자로 사용. 예를들어 맵리듀스에서 키 또는 값이 사용될 필요가 없을 때에 NullWritable 선언될수 있고, 이때 NullWritable 은 빈 상수값으로 효율적으로 저장된다. NullWritable 은 불변하는 싱글톤, NullWritable.get() 을 호출해 사용.

- ObjectWritable 과 GenericWritable
: ObjectWritable 은 자바 프리미티브, String, enum, Writale, null 과 이런 타입의 배열을 위한 일반적인 목적의 래퍼. ObjectWritable 은 어떤 필드가 하나의 타입 이상을 가질때 유용. 예를 들어 SequenceFile 의 값이 다중 타입을 가진다면, 해당 값 타입을 ObjectWritable 로 선언할수 있고, ObjectWritable 에 각 타입을 래핑할수 있다. 일반적인 목적의 매커니즘에서 직렬화할 때마다 래핑된 타입의 클래스 이름을 쓰는 방식은 상당히 공간 낭비가 심하다. 타입수가 적고 미리 알려진 경우에는 타입의 전역 배열을 갖고 배열 참조자를 그 타입에 대한 직렬화된 참조로써 사용하여 공간 낭비를 줄일 수 있다. 이것이 바로 GenericWritable 이 선택한 방법, 지원할 타입을 명시하기 위해 GenericWritable 을 상속받아야 한다.

- Writable 콜렉션
: org.apache.hadoop.io 패키지에는 ArrayWritable, TwoDArrayWritable, MapWritable, SortedMapWritable 네개의 Writable 콜렉션 타입이 있다.
ArrayWritable 과 TwoDArrayWritable 은 Writable 인스턴스 배열과 2차원 배열(배열의 배열) 의 Writable 구현.
MapWritable 과 SortedMapWritable 은 각각 java.util.Map<Writable, Writable> 과 java.util.SortedMap<WritableComparable, Writable> 의 구현이다. MapWritable 과 SortedMapWritable 은 맞춤형 타입에 대한 양수의 바이트 값을 사용.

- 맞춤형 Writable 구현하기
: 맞춤형 Writable 로 바이너리 표현과 정렬순서에 대한 완전한 통제가 가능. 왜냐면, Writable 들은 맵리듀스 데이터 경로의 핵심이고, 바이너리 표현을 튜닝하면 성능상 상당한 효과를 주기 때문.
한쌍의 Text 객체를 저장하는 Writable 구현 (p. 147~149)

- 성능 향상을 위한 RawComparator 구현

- 맞춤형 컴퍼레이터
: 원시컴퍼레이터는 세심한 주의가 필요하다. 자신만의 Writable 을 구현하길 원한다면 추가적인 아이디어를 얻기 위해 org.apache.hadoop.io 패키지의 일부 Writable 의 구현을 살펴볼만하다.
TextPair 바이트 표현의 첫 번째 필드를 비교하기 위한 맞춤형 RawComparator (p. 152)

- 직렬화 프레임워크
: 대부분 맵리듀스 프로그램이 Writable 키와 값 타입을 사용하지만 맵리듀스 API 의 의무사항은 아니다. 사실 모든 타입이 사용될수 있는데, 유일한 요구 사항은 각 타입에 대한 바이너리 표현의 변환 메커니즘의 여부이다. 이것을 지원하기 위해, 하둡에는 플러그인 가능한 직렬화 프레임워크를 위한 API 가 있다. 직렬화 프레임워크는 Serialization(org.apache.hadoop.io.serializer 패키지에 있는) 의 한 구현으로 표현된다.

- 직렬화 IDL(Interface Description Language)
: 언어중립적이고 선언적 방식으로 타입을 정의하는 방식. 하둡 자체의 Record I/O(org.apache.hadoop.record 패키지에서 찾을수 있는)는 Writable 객체 안에 컴파일된 IDL 을 가진다.
(점차 에이브로가 그 역할을 대신하고 있다.)

- 에이브로
: 언어 중립적인 데이터 직렬화 시스템(http://avro.apache.org). 에이브로 데이터는 언어-독립적인 스키마를 사용해 묘사된다. 하지만, 다른 시스템과는 달리, 코드 생성은 에이브로에서 선택사항이다.
에이브로는 '스키마가 항상 존재한다' 고 가정해, 읽을 때나 쓸때 모두 매우 컴팩트하게 데이터를 암호화 할 수 있다.
에이브로 스키마는 보통 JSON 으로 작성하며, 데이터는 바이너리 포맷으로 암호화한다.
에이브로 스펙은 모든 구현체(Implementations)가 지원해야 하는 바이너리 포맷을 정확하게 정의한다.
(스펙이 관여하지 않는 한 가지 영역은 API, 구현체는 에이브로 데이터와 동작하도록 노출할 API를 완전히 자유롭게 정할수 있다. 각 API 는 늘 언어별로 작성되기 때문이다.)
에이브로는 풍부한 스키마 분석 기능을 갖는다. 정의된 제약 조건상에서도, 데이터를 읽는데 사용된 스키마가 쓸때 사용된 스키마와 같은 필요가 없다. (이전 데이터를 읽는 데 사용하는 스키마에 새로운 선택적인 필드를 선언하여 레코드에 추가할 수 있다.)
에이브로 데이터 파일은 스키마가 저장되는 메타데이터 절을 갖으며, 이 메타데이터로 인해 자기기술적인 파일이된다.
에이브로 데이터 파일은 분할 압축 될 수 있다.


- 에이브로 데이터 타입과 스키마
: 에이브로는 적은 수의 데이터 타입을 정의하며, 데이터 타입을 가지고 스키마를 작성해서 애플리케이션-특징적인 데이터 구조를 만들수 있다. 에이브로 프리미티브 타입 (p. 157)
각 프리미티브 타입은 아래처럼 type 속성을 이용해서 더 설명적인 형태로 정의가능
{ "type": "null" }
에이브로 복합 타입 (p. 158)
자바 리플렉트 매핑 (자바 리플렉션을 이용해서 에이브로 타입을 미리 존재하는 자바타입과 매핑시키는 방법.) 에이브로 자바 타입 매핑들 (p. 159~160)

- 인-메모리 직렬화와 역직렬화
: 직렬화와 역직렬화용 API 를 제공. 이 API는 에이브로와 프레임 형식이 이미 정의된 기존의 메시징 시스템과 연동할 때 유용.
에이브로 데이터를 스트림으로 읽고 쓸 수 있는 자바프로그램 (p. 160~162)

- 에이브로 데이터 파일
: 에이브로 객체 컨테이너 파일 포맷은 SequenceFile 처럼 연속적인 에이브로 객체를 저장할 때 사용된다. (에이브로 데이터 파일은 SequenceFile과 달리 모든 언어에 이식이 되도록 설계.)
데이터 파일은 에이브로 스키마를 포함하는 메타데이터가 있는 헤더, 싱크마커와 직렬화된 에이브로 객체들을 연속된 블록들로 구성된다.
블록은 파일 내에서 유일한 싱크마커로 구분되며(특정 파일에 대한 마커는 헤더에서 찾을수 있다.) 이 싱크마커를 이용하면, 파일 내 특정지점을 찾은후, HDFS 의 블록 경계처럼, 블록 경계정보를 가지고 신속하게 재동기화 할 수 있다. 에이브로 객체를 데이터 파일에 쓰기는 스트림에 기록하기와 유사하다.
데이터파일에 기록되는 객체는 파일의 스키마 정의와 일치해야한다.
한가지 중요한 차이점으로, 스키마를 정의할 필요없이 파일의 메타데이터를 참조해서 읽으면 된다는 점을 들수 있다. (DataFileReader 인스턴스의 getSchema() 메소드를 이용하여 스키마를 얻을수 있다.)

- 상호 운영성
: 하나의 언어(파이썬)를 사용해 데이터파일을 만들고 다른언어 (C) 로 다시 읽기. (p. 164~166)

- 스키마 레졸루션
: 데이터를 다시 읽을 때, 쓸 때 사용했던것과 다른스키마를 사용하도록 선택할수 있다.
레코드의 스키마 레졸루션 (p.169)

- 정렬 순서
: Order 속성은 ascending(기본값), descending(역 순), 또는 ignore(필드는 비교 할 때 무시된다) 중 하나를 지정할 수 있다.

- 에이브로 맵리듀스
: org.apache.avro.mapred 패키지의 AvroMapper 와 AvroReducer 는 하둡의(옛날 스타일) Mapper 와 Reducer 클래스의 특별한 구현체이다. (에이브로의 맵리듀스 통합은 두번째 판이 인쇄된 후에 추가될것으로 예상)

- 파일 기반 데이터 구조

- SequenceFile
: 로그파일을 상상하라! 각 로그 레코드는 한 라인의 텍스트다. 바이너리 타입을 로깅하기 원한다면, 하둡의 SequenceFile 클래스는 바이너리 키/값 쌍에 대한 영속적인 데이터 구조를 제공하기 때문에 이러한 상황에 맞는 구조다.

- SequenceFile 쓰기 (p. 172~173)
: 시퀀스파일을 생성하기 위해,  SequenceFile.Writer 인스턴스를 반환하는 createWriter() 전역 메소드 중 하나를 사용한다. 모든 버전에서 쓰기 위한 스트림, Configuration 객체, 해당 키와 값 타입을 명시해야한다.

- SequenceFile 읽기 (p. 175)
: SqeuenceFile.Reader 의 인스턴스를 생성하고, next() 메소드중 하나를 반복 호출하여 레코드를 읽는 과정이라고 할수 있다.
SequenceFile 읽기 (p. 175)
(주목할 것은 getKeyClass() 와 getValueClass() 를 호출하여 SequenceFile.Reader 로 부터 해당 타입을 찾는 방법과ReflectionUtils 가 키와 값에 대한 인스턴스를 생성하기 위해 사용되는 방법)
시퀀스 파일에서 주어진 위치를 탐색하는 첫번째는 그파일의 주어진 위치에 리더를 위치시키는 seek() 메소드. 두번째는 동기화 포인트를 사용하는것. (SequenceFile.Reader 의 sync(long position) 메소드는 position 이후의 다음 번 동기화 포인트로 해당 리더를 위치시킨다.)

- 명령행 인터페이스와 SequenceFile 을 출력하기
: hadoop fs 명령은 텍스트 형식으로 시퀀스 파일을 출력하기 위해 -text 옵션을 제공 (파일의 타입을 검출하고 그것을 텍스트로 적절히 변환하기 위해 그 파일의 매직넘버를 조사)

- SequenceFile 정렬과 병합
: SequenceFile.Sorter 클래스는 다수의 sort() 와 merge() 메소드를 제공하기 때문에 정렬 / 병합을 위한 맵리듀스의 대안이 될 수 있다.(맵리듀스는 시퀀스 파일을 정렬하고 병합하기 위해 선호되는 접근방식)

- SequenceFile 포맷
: 시퀀스파일은 하나의 헤더와 뒤이어 하나 이상의 레코드로 구성되어 있다.
동기화 표시자는 어떤 파일 위치로부터 특정레코드 영역으로 리더를 동기화하는데 사용된다는 것을 상기하라. 각 파일에는 랜덤하게 생성된 동기화 표시자가 있고, 이값은 헤더에 저장된다. 동기화 표시자는 시퀀스파일의 레코드 사이에서 나타난다.
비압축(기본값)이라면, 레코드는 레코드 길이(바이트 단위), 키 길이, 키와 그값으로 이뤄져 있다.
(레코드 압축은 정의된 코덱을 사용해 값의 바이트들을 압축하는 것만 다르다.)
비압축인 경우와 레코드 압축인 경우의 시퀀스 파일의 내부구조(p. 180)
블록압축은 다중 레코드를 한번에 압축한다. 압축밀도가 더 높고 레코드들간 유사성을 이용할 기회를 얻을수 있기 때문에 일반적으로 레코드 압축보다 더 선호되어야 한다. (동기화 표시자는 모든 블록의 시작전에 쓰인다.)
블록 압축일 때의 시퀀스 파일 내부구조(p. 181)

- MapFile
: MapFile은 키검색을 지원하기 위하여 색인과 함께 정렬된 SequenceFile 이다.
MapFile 은 메모리에서 유지되고 있는 Map의 크기 이상으로 증가할수 있는 java.util.Map 의 영속적인 형태로 간주할 수 있다.

- MapFile 쓰기 (p.182~183)
: MapFile.Writer 의 인스턴스를 생성하고 엔트리를 순서대로 추가하기 위해 append() 메소드를 호출한다.
(키는 WritableComparable 의 인스턴스여야 하고 값은 Writable 이어야 하는데 이는 SequenceFile 과 대조되는 부분이다.)
data 와 index 라는 두개의 파일을 포함하는 하나의 디렉터리가 확인된다. (index 파일은 기에 대한 단편을 포함하고 있고, 그 키로 부터 data파일이ㅡ 키 오프셋으로 참조할 수 있는 매핑 정보를 포함한다.)

- MapFile 읽기
: MapFile.Reader 를 생성하고, 파일끝에 도달했기 때문에 더 읽을 엔트리가 없다는 것을 의미하는 false 를 반환할때까지 next() 메소드 호출(SequenceFile 을 위한 프로시져와 동일)
매우 커다란 File 의 색인은 많은 양의 메모리를 차지. 색인 간격을 변경하기 위해 다시 색인하기 보다는 io.map.index.skip 속성을 설정하여 MapFile 을 읽을 때 색인 키에 대한 단편만 메모리로 적재할 수 있다.
(0: 어떠한 색인키도 무시하지 않음, 1: 색인에서 모든 키에 대하여 키 하나를 무시-매두번째 키가 색인에 잡힌다. 2: 키를 두개 무시하는것. 무시할 더 큰 값은 메모리를 절약하지만 평균적으로 더 많은 엔트리가 디스크에서 무시되어야 하므로 검색시간의 비용측면엔 좋지 않다.)

- SequenceFile 을 MapFile 로 변환하기
: MapFile 은 색인되고 정렬된 SequenceFile 이라고 볼수 있다.
MapFile 에 대한 색인 재생성하기 (p. 186~187)
(MapFile의 색인을 다시 생성해주는 전역유틸리티 fix() 가 요점)

Posted by 깜장눈썹