Hive 저장 포맷

저장 포맷

하이브는 두 개의 차원으로 테이블 저장소 관리

  • 로우 포맷 : 행과 특정 행의 필드가 저장된 방식. 직렬자-역직렬자를 혼합한 하이브 전문 용어인 SerDe로 정의. 테이블을 질의하는 경우와 같이 역직렬화를 수행할 때 SerDe는 파일에 저장된 바이트의 데이터행을 하이브에서 내부적으로 사용되는 객체로 역직렬화하여 그 데이터에 대한 연산을 수행한다. INSERT나 CTAS를 수행하는 경우와 같이 직렬화를 수행할 때 하이브에서 내부적으로 표현되는 데이터 행은 출력 파일에 저장될 바이트로 직렬화된다.


  • 파일 포맷 : 행에 포함된 필드의 포맷을 지시. 가장 단순한 포맷은 일반텍스트 파일 포맷이지만, 행 기반과 컬럼 기반 바이너리 포맷도 사용할 수 있다.


기본 저장 포맷 : 구분 텍스트

ROW FORAMT이나 STORED AS 절을 지정하지 않고 테이블을 생성하면 테이블의 포맷은 기본 포맷인 한 줄이 한 행이 되는 구분 텍스트가 된다.

하이브에서 구분자로 확장 문자를 사용하면 문제가 생길 수 있기 때문에 데이터 필드에서 거의 사용되지 않는 문자를 선택하는 것이 좋다.

ARRAY 또는 STRUCT의 항목이나 MAP의 키-값 쌍을 구분하기 위해 사용되는 기본 컬렉션 항목의 구분자는 Ctrl-B 문자다. MAP에서 키와 값을 구분하기 위해 사용되는 기본적인 맵 키의 구분자는 Ctrl-C 문자다.

구분자의 선행 기술은 기본형만 포함하고 있는 복합형의 나열적 데이터 구조에 적합하다. 그러나 중첩형은 조금 다른데 중첩 수준에 따라 다른 구분자를 사용해야 한다.

예를 들어 배열의 배열이면 외부 배열의 구분자는 Ctrl-B 문자고 내부 배열의 구분자는 그다음 구분자인 Ctrl-C 문자다. 만일 특정 중첩 구조에서 하이브가 사용하는 구분자를 모르겠으면 다음과 같은 명령을 실행해보면 된다.

CREATE TABLE nested
AS
SELECT array(array(1, 2), array(3,4))
FROM dummy;

먼저 테이블을 생성한 후 출력 파일의 구분자를 검사하기 위한 hexdump 등의 명령을 실행한다.

하이브는 실제로 아스키코드 1부터 8에 해당하는 8단계의 구분자를 제공하지만 앞에 있는 3개의 구분자만 재정의할 수 있다.

다음 구문은

CREATE TABLE ...;

아래와 같이 보다 명시적으로 지정할 수 있다.

CREATE TABLE ...
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\001'
  COLLECTION ITEMS TERMINATED BY '\002'
  MAP KEYS TERMINATED BY '\003'
  LINES TERMINATED BY '\n'
STORED AS TEXTFILE;

구분 문자를 8진수 형태로 사용할 수 있다. 예를 들어 Ctrl-A는 001로, Ctrl-B는 002로 사용할 수 있다.

내부적으로 하이브는 행 기반 맵리듀스 텍스트 입출력 포맷과 더불어 LazySimpleSerDe로 명명된 SerDe를 구분 포맷을 위해 사용한다. 접두사 ‘lazy’는 필드가 실제 사용되는 시점에서만 역직렬화되기 때문에 붙여진 것이다. 그러나 Boolean형은 필드의 값이 true 또는 false와 같은 산문체 형식으로 저장되기 때문에 LazySimpleSerDe는 간결 포맷은 아니다.

저장 포맷의 단순함은 맵리듀스 프로그램이나 스트리밍을 포함한 다양한 도구로 데이터를 쉽게 처리할 수 있다는 장점이 있다. 그러나 사용자가 고려해볼 만한 매우 간결하고 실용적인 바이너리 저장 포맷도 있다.



바이너리 저장 포맷 : 시퀀스 파일, 에이브로 데이터 파일, 파케이 파일, RCFile, ORCFile

바이너리 포맷을 사용할 때는 CREATE TABLE 구문에서 STORED AS 절만 변경하면 된다. 바이너리 포맷은 행의 형식이 특정 바이너리 포맷에 따라 결정되므로 ROW FORMAT을 지정할 필요가 없다.

바이너리 포맷은 행 기반 포맷과 컬럼 기반 포맷으로 크게 구분된다. 일반적으로 컬럼 기반 포맷은 해당 테이블의 소수의 컬럼만 접근할 때 적합하고, 행 기반 포맷은 한 행에 있는 대부분의 컬럼에 모두 접근할 때 적합하다고 할 수 있다.

하이브에서 기본으로 제공하는 행 기준 포맷은 에이브로 데이터파일과 시퀀스 파일이 있다. 둘 다 범용, 분할 가능, 압축 포맷이다. 에이브로는 추가로 스키마 확장 및 다중 언어 바인딩을 제공한다. 에이브로 포맷은 하이브 0.14.0 부터 지원한다.

SET hive.exec.compress.output=true;
SET avro.output.codec=snappy;
CREATE TABLE ... STORED AS AVRO;

위 예제와 같이 관련 속성을 설정하면 압축을 활성화시킬 수 있다.

이와 유사하게 하이브에서 시퀀스 파일을 사용하려면 STORED AS SEQUENCEFILE 선언을 하면 된다.

하이브는 컬럼 기반 포맷으로 파케이, RCFile, ORCFile을 제공한다.

CREATE TABLE users_parquet STORED AS PAQUET
AS
SELECT * FROM users;



커스텀 SerDe 사용 예: RegexSerDe

데이터를 로드하기 위한 커스텀 SerDe의 사용법을 살펴보자. 텍스트 파일에서 고정폭의 관측소 메타데이터를 읽기 위해 정규표현식을 사용하는 SerDe를 사용할 것이다.

CREATE TABLE stations (usaf STRING, wban STRING, name STRING)
ROW FORMAT SERDE 'org.apache.hadoop.hive.contirb.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
  "input.regex" = "(\\\d{6}) (\\d{5}) (,{29}) .*"
);

이전에 나온 구분 텍스트 예제에서는 구분자로 분리된 텍스트를 참조하기 위해 ROW FORMAT 절에 DILIMITED 키워드를 사용했다. 이번 예제에서는 그 대신 SERDE 키워드완 SerDe를 구현한 자바 클래스의 전체 경로를 포함한 클래스명(org.apache.hadoop.hive.contrib.serde2.RegexSerDe) 으로 SerDe를 지정했다.

SerDe는 WITH SERDEPROPERTIES 절을 사용하여 추가적인 속성을 설정할 수 있다. 이 예제에서는 regexSerDe로 한정된 input.regex 속성을 설정했다.

input.regex는 행을 이루는 한 줄의 텍스트를 컬럼 집합으로 전환하는 역직렬화 과정에서 사용되는 정규 표현식 패턴이다. 여기서 자바 정규표현식 구문은 일치하는 문자열을 추출하기 위함이고, 각 컬럼은 괄호로 묶인 부분을 갈무리하여 얻게 된다.

테이블 로드 시 LOAD DATA 구문

LOAD DATA LOCAL INPATH "/hive/stage/table/"
INTO TABLE STATIONS;

LOAD DATA는 하이브의 웨어하우스 디렉터리로 파일을 복사하거나 이동한다. 로드 연산에는 SerDe가 사용되지 않는다.

테이블에서 데이터를 검색할 때 역직렬화를 위해 SerDe가 호출된다.

SELECT * FROM stations LIMIT 4;

RegexSerDe 는 하이브로 데이터를 가져올 때는 유용하지만 효율성은 떨어지기 때문에 범용 저장소로 사용하면 안 된다. 그 대신 데이터를 바이너리 포맷으로 복사하는 방식을 권장한다.



저장소 제어기

저장소 제어기는(storage handler)는 HBase와 같이 하이브가 기본적으로 접근할 수 없는 다른 저장소 시스템에 사용된다. 저장소 제어기는 ROW FORMAT과 STORED AS 절을 이용하여 지정된다.