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