AvroSerDe

AvroSerDe

AvroSerDe는 Avro 데이터를 Hive 테이블로 읽고 쓸 수 있게 해주는 SerDe예요. Avro 스키마와 Hive 테이블 스키마를 자동 변환해 주기 때문에 JSON처럼 구조적인 데이터를 다루기 좋아요.

출처: 문서

본문

사용 가능한 버전

AvroSerde는 Hive 0.9.1 이상에서 사용할 수 있습니다.

개요 – Hive에서 Avro 다루기

AvroSerde는 사용자가 Avro 데이터를 Hive 테이블처럼 읽거나 쓸 수 있게 해줍니다. AvroSerde의 특징:

  • Avro 스키마에서 Hive 테이블의 스키마를 유추합니다. Hive 0.14부터는 Hive 테이블 스키마에서 Avro 스키마를 유추할 수도 있습니다.
  • Avro의 하위 호환성(backwards compatibility) 기능을 활용해, 지정된 스키마에 대해 테이블 내의 모든 Avro 파일을 읽습니다.
  • 임의로 중첩된 스키마를 지원합니다.
  • 모든 Avro 데이터 타입을 동등한 Hive 타입으로 변환합니다. 대부분의 타입은 정확히 매핑되지만, 일부 Avro 타입은 Hive에 존재하지 않아 AvroSerde가 자동으로 변환합니다.
  • 압축된 Avro 파일을 이해합니다.
  • nullable 타입을 Union[T, null]으로 다루는 Avro 관용구를 투명하게 단순한 T로 변환하고, 적절할 때 null을 반환합니다.
  • 모든 Hive 테이블을 Avro 파일로 씁니다.
  • 이 프로젝트의 ETL 과정에서 가장 복잡한 Avro 스키마에 대해 안정적으로 동작했습니다.
  • Hive 0.14부터 Alter Table 문을 사용해 Avro 기반 Hive 테이블에 컬럼을 추가할 수 있습니다.

SerDe에 대한 일반 정보는 개발자 가이드의 Hive SerDe를 참고하세요. 입출력 처리에 대한 자세한 내용은 SerDe도 참고하세요.

요구 사항

AvroSerde는 Hive 0.9.1 이상에서 빌드·테스트되었으며, Hive 0.13과 0.14부터 Avro 1.7.5를 사용합니다.

Hive 버전 Avro 버전
Hive 0.9.1 Avro 1.5.3
Hive 0.10, 0.11, 0.12 Avro 1.7.1
Hive 0.13, 0.14 Avro 1.7.5

Avro → Hive 타입 변환

대부분의 Avro 타입은 동등한 Hive 타입으로 직접 변환되지만, Hive에 존재하지 않는 타입은 합리적인 동등 타입으로 변환됩니다. 또한 AvroSerde는 null과 다른 타입의 유니언을 특별히 처리하는데, 아래에서 설명합니다:

Avro 타입 변환되는 Hive 타입 참고
null void
boolean boolean
int int
long bigint
float float
double double
bytes binary Hive 0.12.0 이전에는 Array[smallint] 로 변환됨
string string
record struct
map map
list array
union union [T, null] 유니언은 투명하게 nullable T로 변환되고, 다른 타입들은 해당 타입들의 Hive 유니언으로 직접 변환됩니다. 다만 유니언은 Hive 7에서 도입되었으며 현재 where/group-by 문에서 사용할 수 없습니다. 본질적으로 보기만 가능(look-at-only)합니다. AvroSerde가 [T,null]을 nullable T로 투명하게 변환하므로, 이 제한은 여러 타입의 유니언 또는 단일 타입과 null이 아닌 유니언에만 적용됩니다.
enum string Hive에는 enum 개념이 없습니다.
fixed binary Hive 0.12.0 이전에는 Array[smallint] 로 변환됨

Avro 기반 Hive 테이블 만들기

Avro 기반 테이블은 AvroSerDe를 사용해 Hive에서 만들 수 있습니다.

모든 Hive 버전

Avro 기반 테이블을 만들려면 serde를 org.apache.hadoop.hive.serde2.avro.AvroSerDe로, inputformat을 org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat으로, outputformat을 org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat으로 지정합니다. 또한 AvroSerde가 테이블의 최신 스키마를 가져올 위치를 제공합니다. 예를 들어:

CREATE TABLE kst
  PARTITIONED BY (ds string)
  ROW FORMAT SERDE
  'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
  STORED AS INPUTFORMAT
  'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'
  OUTPUTFORMAT
  'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat'
  TBLPROPERTIES (
    'avro.schema.url'='http://schema_provider/kst.avsc');

이 예제에서는 웹서버에서 source-of-truth 리더 스키마를 가져옵니다. 스키마를 제공하는 다른 옵션은 아래에서 설명합니다.

표준 Hive 작업으로 Avro 파일을 데이터베이스에 추가하거나(또는 외부 테이블 생성) DML을 사용합니다.

이 테이블은 아래와 같은 설명으로 나타날 수 있습니다:

hive> describe kst;
OK
string1 string  from deserializer
string2 string  from deserializer
int1    int     from deserializer
boolean1        boolean from deserializer
long1   bigint  from deserializer
float1  float   from deserializer
double1 double  from deserializer
inner_record1   struct<int_in_inner_record1:int,string_in_inner_record1:string> from deserializer
enum1   string  from deserializer
array1  array<string>   from deserializer
map1    map<string,string>      from deserializer
union1  uniontype<float,boolean,string> from deserializer
fixed1  binary  from deserializer
null1   void    from deserializer
unionnullint    int     from deserializer
bytes1  binary  from deserializer

이 시점부터 Avro 기반 테이블은 Hive에서 다른 테이블처럼 사용할 수 있습니다.

Hive 0.14 이상 버전

Hive 0.14부터는 DDL 문에서 "STORED AS AVRO"를 사용하면 Avro 기반 테이블을 간단히 만들 수 있습니다. AvroSerDe가 Hive 테이블 스키마로부터 적절한 Avro 스키마를 만들어 주는데, 이는 Hive에서 Avro를 사용하는 데 큰 이점입니다.

예를 들어:

CREATE TABLE kst (
    string1 string,
    string2 string,
    int1 int,
    boolean1 boolean,
    long1 bigint,
    float1 float,
    double1 double,
    inner_record1 struct<int_in_inner_record1:int,string_in_inner_record1:string>,
    enum1 string,
    array1 array<string>,
    map1 map<string,string>,
    union1 uniontype<float,boolean,string>,
    fixed1 binary,
    null1 void,
    unionnullint int,
    bytes1 binary)
  PARTITIONED BY (ds string)
  STORED AS AVRO;

이 테이블은 아래와 같은 설명으로 나타날 수 있습니다:

hive> describe kst;
OK
string1 string  from deserializer
string2 string  from deserializer
int1    int     from deserializer
boolean1        boolean from deserializer
long1   bigint  from deserializer
float1  float   from deserializer
double1 double  from deserializer
inner_record1   struct<int_in_inner_record1:int,string_in_inner_record1:string> from deserializer
enum1   string  from deserializer
array1  array<string>   from deserializer
map1    map<string,string>      from deserializer
union1  uniontype<float,boolean,string> from deserializer
fixed1  binary  from deserializer
null1   void    from deserializer
unionnullint    int     from deserializer
bytes1  binary  from deserializer

테이블을 Avro 파일로 쓰기

AvroSerde는 어떤 Hive 테이블이든 Avro 파일로 직렬화할 수 있습니다. 사실상 any-Hive-type → Avro 변환기입니다. 테이블을 Avro 파일로 쓰려면 먼저 적절한 Avro 스키마를 만들어야 합니다(아래 설명처럼 Hive 0.14.0 이상에서는 예외). CREATE TABLE AS SELECT 유형의 문은 현재 지원되지 않습니다.

타입 변환은 위 표에 자세히 나와 있습니다. 직접 변환되지 않는 타입에 대해 기억할 몇 가지 사항:

  • null일 수 있는 타입은 Avro에서 해당 타입과 Null의 유니언으로 정의해야 합니다. 그렇게 정의되지 않은 필드에 null이 오면 저장 중 예외가 발생합니다. Hive의 모든 필드는 null일 수 있으므로, 이를 지원하기 위해 Hive 스키마를 변경할 필요는 없습니다.
  • Avro Bytes 타입은 Hive에서 tiny int 리스트로 정의해야 합니다. AvroSerde는 저장 과정에서 이를 Bytes로 변환합니다.
  • Avro Fixed 타입은 Hive에서 tiny int 리스트로 정의해야 합니다. AvroSerde는 저장 과정에서 이를 Fixed로 변환합니다.
  • Avro Enum 타입은 Hive에 enum 개념이 없으므로 Hive에서 문자열로 정의해야 합니다. 테이블에 유효한 enum 값만 존재하도록 하세요. 정의되지 않은 enum을 저장하려 하면 예외가 발생합니다.

Hive는 타입에 대해 매우 관대합니다. 제공된 컬럼과 일치하는 모든 값을 새 테이블의 동등한 컬럼 위치에 저장하려 시도합니다. 예를 들어 컬럼 이름에 대한 일치는 수행되지 않습니다. 따라서 쿼리 작성자가 대상 컬럼 타입이 올바른지 확인할 책임이 있습니다. 타입이 올바르지 않으면 Avro가 그 타입을 받아들일 수도, 예외를 던질 수도 있습니다. 이는 특정 타입 조합에 따라 다릅니다.

예제

모든 Hive 데이터 타입을 포함하는 다음 Hive 테이블을 고려해 보세요. 좋은 예가 됩니다:

CREATE TABLE test_serializer(string1 STRING,
                             int1 INT,
                             tinyint1 TINYINT,
                             smallint1 SMALLINT,
                             bigint1 BIGINT,
                             boolean1 BOOLEAN,
                             float1 FLOAT,
                             double1 DOUBLE,
                             list1 ARRAY<STRING>,
                             map1 MAP<STRING,INT>,
                             struct1 STRUCT<sint:INT,sboolean:BOOLEAN,sstring:STRING>,
                             union1 uniontype<FLOAT, BOOLEAN, STRING>,
                             enum1 STRING,
                             nullableint INT,
                             bytes1 BINARY,
                             fixed1 BINARY)
 ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' COLLECTION ITEMS TERMINATED BY ':' MAP KEYS TERMINATED BY '#' LINES TERMINATED BY '\n'
 STORED AS TEXTFILE;

테이블이 다음과 같은 csv 파일로 뒷받침된다면:

| why hello there | 42 | 3 | 100 | 1412341 | true | 42.43 | 85.23423424 | alpha:beta:gamma | Earth#42:Control#86:Bob#31 | 17:true:Abe Linkedin | 0:3.141459 | BLUE | 72 | ^A^B^C | ^A^B^C | | another record | 98 | 4 | 101 | 9999999 | false | 99.89 | 0.00000009 | beta | Earth#101 | 1134:false:wazzup | 1:true | RED | NULL | ^D^E^F^G | ^D^E^F | | third record | 45 | 5 | 102 | 999999999 | true | 89.99 | 0.00000000000009 | alpha:gamma | Earth#237:Bob#723 | 102:false:BNL | 2:Time to go home | GREEN | NULL | ^H | ^G^H^I |

아래 설명대로 이를 Avro로 쓸 수 있습니다.

모든 Hive 버전

이 테이블을 Avro 파일로 저장하려면 동등한 Avro 스키마를 만드세요(namespace와 record의 실제 이름은 중요하지 않습니다):

{
  "namespace": "com.linkedin.haivvreo",
  "name": "test_serializer",
  "type": "record",
  "fields": [
    { "name":"string1", "type":"string" },
    { "name":"int1", "type":"int" },
    { "name":"tinyint1", "type":"int" },
    { "name":"smallint1", "type":"int" },
    { "name":"bigint1", "type":"long" },
    { "name":"boolean1", "type":"boolean" },
    { "name":"float1", "type":"float" },
    { "name":"double1", "type":"double" },
    { "name":"list1", "type":{"type":"array", "items":"string"} },
    { "name":"map1", "type":{"type":"map", "values":"int"} },
    { "name":"struct1", "type":{"type":"record", "name":"struct1_name", "fields": [
          { "name":"sInt", "type":"int" }, { "name":"sBoolean", "type":"boolean" }, { "name":"sString", "type":"string" } ] } },
    { "name":"union1", "type":["float", "boolean", "string"] },
    { "name":"enum1", "type":{"type":"enum", "name":"enum1_values", "symbols":["BLUE","RED", "GREEN"]} },
    { "name":"nullableint", "type":["int", "null"] },
    { "name":"bytes1", "type":"bytes" },
    { "name":"fixed1", "type":{"type":"fixed", "name":"threebytes", "size":3} }
  ] }

그런 다음 다음으로 Avro에 쓸 수 있습니다:

CREATE TABLE as_avro
  ROW FORMAT SERDE
  'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
  STORED as INPUTFORMAT
  'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'
  OUTPUTFORMAT
  'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat'
  TBLPROPERTIES (
    'avro.schema.url'='file:///path/to/the/schema/test_serializer.avsc');
 
INSERT OVERWRITE TABLE as_avro SELECT * FROM test_serializer;

Hive 0.14 이상

Hive 0.14 이상 버전에서는 Avro 스키마를 수동으로 만들 필요가 없습니다. 위에서 테이블을 Avro 파일로 저장한 절차는 DDL 문 하나와 그 뒤의 insert로 줄어듭니다.

CREATE TABLE as_avro(string1 STRING,
                     int1 INT,
                     tinyint1 TINYINT,
                     smallint1 SMALLINT,
                     bigint1 BIGINT,
                     boolean1 BOOLEAN,
                     float1 FLOAT,
                     double1 DOUBLE,
                     list1 ARRAY<STRING>,
                     map1 MAP<STRING,INT>,
                     struct1 STRUCT<sint:INT,sboolean:BOOLEAN,sstring:STRING>,
                     union1 uniontype<FLOAT, BOOLEAN, STRING>,
                     enum1 STRING,
                     nullableint INT,
                     bytes1 BINARY,
                     fixed1 BINARY)
STORED AS AVRO;
INSERT OVERWRITE TABLE as_avro SELECT * FROM test_serializer;

Avro 파일 확장자

Hive 작업이 쓰는 파일은 유효한 Avro 파일이지만, MapReduce는 표준 .avro 확장자를 붙이지 않습니다. 이 파일들을 복사한다면 .avro로 이름을 바꾸는 편이 좋습니다.

테이블의 Avro 스키마 지정하기

Avro 테이블의 리더 스키마를 제공하는 방법은 세 가지가 있으며, 모두 serde에 대한 매개변수와 관련됩니다. 스키마가 진화함에 따라 테이블의 매개변수를 업데이트하여 이 값을 변경할 수 있습니다.

avro.schema.url 사용

스키마를 가져올 URL을 지정합니다. http 스키마의 경우 테스트와 소규모 클러스터에는 동작하지만, 스키마는 작업의 각 태스크에서 최소 한 번씩 접근되므로 작업이 URL 제공자(예: 웹서버)에 대한 DDoS 공격이 될 수 있습니다. 테스트 외의 용도로 이 매개변수를 사용할 때는 주의하세요.

스키마가 HDFS의 위치를 가리킬 수도 있습니다. 예: hdfs://your-nn:9000/path/to/avsc/file. 그러면 AvroSerde는 HDFS에서 파일을 읽으며, 이는 동시 다중 읽기에 대한 복원력을 제공해야 합니다. serde는 모든 mapper에서 이 파일을 읽으므로, 스키마 파일의 복제(replication)를 높은 값으로 설정해 리더에게 좋은 locality를 제공하는 것이 좋습니다. 스키마 파일 자체는 상대적으로 작아야 하므로 프로세스에 큰 오버헤드를 추가하지 않습니다.

schema.literal 사용, create 문에 스키마 삽입

create 문에 스키마를 직접 포함시킬 수 있습니다. Hive가 매개변수 값을 정의하는 데 이를 사용하므로, 스키마에 작은따옴표가 없거나(또는 적절히 이스케이프된 경우) 이 방식이 동작합니다. 예를 들어:

CREATE TABLE embedded
  COMMENT "just drop the schema right into the HQL"
  ROW FORMAT SERDE
  'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
  STORED AS INPUTFORMAT
  'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'
  OUTPUTFORMAT
  'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat'
  TBLPROPERTIES (
    'avro.schema.literal'='{
      "namespace": "com.howdy",
      "name": "some_schema",
      "type": "record",
      "fields": [ { "name":"string1","type":"string"}]
    }');

값이 작은따옴표로 둘러싸여 create 문에 그대로 붙여넣어진다는 점에 주의하세요.

avro.schema.literal 사용, 스키마를 스크립트로 전달

Hive는 간단한 변수 치환을 할 수 있으며 변수에 포함된 스키마를 스크립트로 전달할 수 있습니다. 이렇게 하려면 스키마를 완전히 이스케이프해야 합니다(캐리지 리턴은 \n으로, 탭은 \t로, 따옴표는 이스케이프 등). 예:

set hiveconf:schema;
DROP TABLE example;
CREATE TABLE example
  ROW FORMAT SERDE
  'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
  STORED AS INPUTFORMAT
  'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'
  OUTPUTFORMAT
  'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat'
  TBLPROPERTIES (
    'avro.schema.literal'='${hiveconf:schema}');

이 스크립트 파일을 실행하려면($SCHEMA가 이스케이프된 스키마 값으로 정의되어 있다고 가정):

hive --hiveconf schema="${SCHEMA}" -f your_script_file.sql

$SCHEMA가 스키마 내의 공백을 올바르게 처리하기 위해 따옴표 안에서 보간(interpolate)된다는 점에 주의하세요.

none을 사용해 avro.schema.literal 또는 avro.schema.url 무시하기

Hive는 속성을 해제하거나 제거하는 쉬운 방법을 제공하지 않습니다. URL 또는 스키마 사용에서 다른 방식으로 전환하려면 무시할 값을 none으로 설정하면 AvroSerde가 설정되지 않은 것처럼 취급합니다.

HBase 통합

Hive 0.14.0부터는 HBase 컬럼에 저장된 Avro 객체를 Hive에 struct로 보이게 만들어 저장·조회할 수 있습니다. 이를 통해 Hive가 깊게 구조화될 수 있는 HBase 데이터에 대해 임시(ad hoc) 분석을 수행할 수 있습니다. 0.14.0 이전에는 HBase-Hive 통합이 컬럼의 기본 데이터 타입만 쿼리할 수 있었습니다. 자세한 내용은 Avro Data Stored in HBase Columns 문서를 참고하세요.

문제가 생겼을 때

Hive는 작업 제출 전에 AvroSerde에서 발생한 예외를 삼키는 경향이 있습니다. Hive를 더 장황하게 만들려면 _hive –hiveconf hive.root.logger=INFO,console _ 로 시작하세요. 그러면 콘솔에 훨씬 많은 정보가 출력되며, 여기에 AvroSerde가 무엇이 잘못됐는지 알려주려는 정보가 포함될 가능성이 높습니다. AvroSerde가 MapReduce 중 오류를 만나면 스택 트레이스가 실패한 태스크 로그에 제공되며, JobTracker의 웹 인터페이스에서 확인할 수 있습니다. AvroSerde는 AvroSerdeException만 발생시킵니다. 이것을 찾아보세요. 버그 리포트에 이를 포함해 주세요. 가장 흔한 것은 Avro가 기대하는 것과 호환되지 않는 타입을 직렬화하려 할 때 발생하는 예외일 것으로 예상됩니다.

FAQ

  • 테이블을 describe하거나 쿼리할 때 error-error-error-error-error-error-error 와 avro.schema.literal과 avro.schema.url을 확인하라는 메시지가 나타나는 이유는 무엇인가요?

AvroSerde는 avro.schema.literal 또는 avro.avro.schema.url 값으로 제공된 스키마를 찾거나 파싱하는 데 문제가 있을 때 이 메시지를 반환합니다. Hive가 serde 구성 메서드에 대한 모든 호출이 성공할 것을 기대하기 때문에 실제 예외를 반환할 수 없어 더 구체적으로 알려줄 수 없습니다. 이 메시지로 오류를 알림으로써 테이블은 좋은 상태로 남고, 잘못된 값은 alter table T set TBLPROPERTIES 호출로 수정할 수 있습니다.

더 알아보기 (Learn more)

SerDe의 일반적인 개념과 입출력 처리 원리를 이해하려면 SerDe 문서를 참고해요. 다른 포맷 SerDe로는 CSV SerDe도 있으니 함께 읽어보면 전체 그림이 잡혀요.