(하둡) 압축

1. 압축 


파일 압축은 저장공간을 줄이고, 네트워크 또는 디스크로부터 데이터 전송을 고속화 할 수 있는 두가지 커다란 이점이 있다.

압축포맷 도구 알고리즘 파일 확장명 분할 가능
DEFLATE N/A DEFLATE .deflate No
gzip gzip DEFLATE .gz No
bzip2 bzip2 bzip2 .bz2 Yes
LZO lzop LZO .lz0 No
LZ4 N/A LZ4 .lz4 No
Snappy N/A Snappy .snappy No



모든 압축 알고리즘은 압축과 해제가 빨라 질 수록 공간이 늘어나는 희생을 감수해야 하기 때문에 공간과 시간은 트레이드 오프 관계에 있다.
위 표의 도구는 보통 9개의 옵션(-1은 스피드 최적화, -9는 공간 최적화를 의미)를 제공함으로써 어느정도 이러한 트레이드 오프에 대한 제어를 가능하게 한다.

다음 명령은 가장 빠른 압축 메소드를 사용해서 file.gz라는 압축 파일을 생성한다.

%gzip -1 file


각 도구는  각기 다른 압축 특성이 있다. 
gzip은 일반적인 목적의 압축 도구이고, 공간/시간 트레이드 오프의 중앙에 위치한다.
bzip2는 gzip보다 더 효율적으로 압축하지만 대신 더 느리다.
bzip2의 압축 해제 속도는 압축속도보다 더 빠르지만 여전히 다른 포맷에 비해 더 느리다.
한편 LZO, LZ4, Snappy 는 모두 속도에 최적화 되었고, gzip보다 어느정도 빠르지만 압축 효율은 떨어진다. Snappy와 LZ4는 압축 해제 속도 측면에서 LZO보다 매우 빠르다

'분할 가능' 열은 압축 포맷이 분할을 지원하는지 여부를 알려준다. 예를들어, 스트림의 특정 지점까지 탐색한 후 이후의 일부 지점으로 부터 읽기를 시작할 수 있는지 알려준다.
분할 가능한 압축 포맷은 특히 맵리듀스에 적합하다.


2. 코덱


코덱은 압축-해제 알고리즘을 구현한 것이다. 하둡에서 코덱은 CompressionCodec 인터페이스로 구현된다. 예를들어, GzipCodec은 gzip 을 위한 압축과 해제 알고리즘을 담고 있다.
다음표는 하둡에서 이용할 수 있는 코덱을 보여준다.

압축포맷 하둡압축코덱
DEFLATE org.apache.hadoop.io.compress.DefaultCodec
Gzip org.apache.hadoop.io.compress.GzipCodec
bzip2 org.apache.hadoop.io.compress.bzip2Codec
LZO org.apache.hadoop.io.compress.LzopCodec
LZ4 org.apache.hadoop.io.compress.Lz4Codec
Snappy org.apache.hadoop.io.compress.SnappyCodec


* CompressionCodec을 통한 압출 및 해제 스트림


CompressionCodec은 데이터를 쉽게 압축하거나 해제해주는 두개의 메소드를 제공한다.
출력 스트림에 쓸 데이터를 압축하려면 createOutputStream(OutputStreamout ot)메소드를 사용하는데, 이 메소드는 압축되지 않은 데이터를 압축된 형태로 내부 스트림에 쓰는 CompressionOutputStrean 을 생성한다.

반대로 입력 스트림으로부터 읽어 들인 데이터를 압축 해제하려면 createInputStream(InputStream in)메소드를 호출하는데, 이 메소드는 기존 스트림으로부터 비 압축 데이터를 읽을 수 있는 CompressionInputStream을 반환한다.



* CompressionCodeFactory를 사용하여 CompressionCodec 유추하기

압축된 파일을 읽을 때 일반적으로 해당 파일 확장명을 보면 사용된 코덱을 유추할 수 있다.

CompressionCodecFactory의 getCodec()메소드는 지정된 파일에 대한 Path객체를 인자로 받아 파일 확장명에 맞는 CompressionCodec을 찾아준다.

* 원시 라이브러리

성능 관점에서 압축과 해제를 위해 원시 라이브러리를 사용하는 것이 바람직하다.
예를들어 원시 gzip라이브러리를 사용하여 테스트 해본 결과 압축 해제 성능은 최대 50%, 압축성능은 거의 최대 10%정도 더 좋아졌다.
각 압축 포맷에 대한 자바와 원시 구현체의 사용 가능 여부를 보여 준다.
모든 포맷은 원시구현체를 가지고 있지만 모든 포맷이 자바 구현체를 가지고 있는 것은 아니다.

압축 포맷          자바 구현체          원시 구현체
DEFLATE             YES                      YES
gzip                     YES                      YES
bzip2                   YES                      YES
LZO                      NO                      YES
LZ4                      NO                       YES
Snappy                NO                       YES


하둡은 lib/native 디렉토리에 미리 빌드된 64비트 리눅스용 원시 압축 라이브러리인 libhadoop.so를 제공한다. 다른 플랫폼은 소스 최상단의 BUILDING.txt 문서의 지시에 따라 직접 라이브러리를 컴파일해야 한다.
원시 라이브러리는 java.library.path라는 자바 시스템 속성에 따라 선택된다.
etc/hadoop 디렉토리 있는 hadoop 스크립트 이 속성을 직접 설정해주지만 , 이 스크립트를 사용하지 않게는다면 애플리케이션에서 설정할 수도 있다.

기본적으로 하둡은 자신이 수행되는 플랫폼에 맞는 원시 라이브러리를 먼저 찾고, 있으면 자동으로 해당 라이브러리를 로드한다. 이것은 원시 라이브러리를 사용하기 위해 어떠한 환경 설정 도 변경할 필요가 없음을 의미한다. 그러나 압축 관련 문제를 디버깅 하는 것과 같은 상황에서는 원시 라이브러리의 사용을 비활성화 하고 싶을 수도 있다.
이를 위해 io.native.lib.available 속성을 false로 설정하면 되는데, 이때 내장되어있는 자바와 동등한 콛ㄱ 라이브러리가 사용된다.

코덱풀 만일 원시 라이브러리를 사용하고 애플리케이션에서 상당히 많은 압축 또는 해제 작업을 수행해야 한다면 압축기와 해제기를 재사용해서 객체 생성 비용을 절감할 수 있는 CodecPool의 사용을 고려하는 것이좋다.



3. 압축과 입력 스플릿

맵 리듀스로 처리되는 데이터를 어떻게 압축할지 고민하는 시점에 압축 포맷이 분할을 지원하는지 여부를 알고 있는 것이 중요하다.
HDFS에 1GB 크기로 저장된 비압축 파일을 고려해보자.
128MB 크기의 HDFS 블록 8개가 저장되어 있을 때 이 파일을 입력으로 사용하는 맵리듀스 잡은 개별적인 맵태스크에서 독립적으로 처리되는 8개의 입력 스플릿을 생성할 것이다. 

이번에는 그 파일이 압축된 크기가 1GB인 단일 gzip 압축 파일이라고 상상해보자. 이전처럼
HDFS는 그 파일을 8개의 블록으로 저장할 것이다. 그러나 gzip스트림이 특정 위치에서 읽기를 지원하지 않기 때문에 각 블록별로 스플릿을 생성할 수 없다. 그러므로 맵 태스크가 각 블록 스플릿을 개별적으로 읽는것은 불가능하다. gzip포맷은 압축된 데이터를 저장하기 위해 DEFLATE를 사용하고, DEFLATE는 데이터를 일련의 압축된 블록으로 저장한다. 
리더가 다음블록의 시작으로 이동하려면 스트림과 동기화되어 그 스트림의 특정 지점에 있을 수 있는 어떤 방법을 지원해야 하는데,  DEFLATE압축 방식은 각 블록의 시작점을 구별할 수 없다는 문제가 있다.
이러한 이유로 gzip은 분할을 지원하지 않는다.



댓글

이 블로그의 인기 게시물

(18장) WebSocekt과 STOMP를 사용하여 메시징하기

(네트워크)폴링방식 vs 롱 폴링방식

(ElasticSearch) 결과에서 순서 정렬