티스토리 뷰

Avro

Apache Avro - 설명

소농배 2022. 2. 17. 12:27

Primitive Types

  • null : 값이 없음
  • boolean : a binary 값
  • int : 32-bit signed integer
  • long : 64-bit signed integer
  • float : single precision (32-bit) IEEE 754 소숫점 숫자
  • double : double precision (64-bit) IEEE 소숫점 숫자
  • bytes : 8-bit unsigned 바이트 배열
  • string : unicode characther 배열

Primitive 타입은 속성을 갖지 않는다.

Primitive 타입 이름은 또한 type name 으로 정의된다. 그래서 예를들어 "string" Schema 는 {"type":"string"} 과 동일하다.

 

Complex Types

 Avro 는 6개 종류의 Complext Type 을 지원한다 : records, enums, arrays, maps, unions, fixed

Records

 Records 는 타입 이름으로 "record" 를 사용하며 아래 요소들을 지원한다.

  • name : Record 의 이름을 제공하는 JSON string (필수)
  • namespace : name 을 보충하는 JSON string
  • doc : Schema 사용에게 문서를 제공하는 JSON string (선택)
  • aliases : Record 의 또 다른 이름을 제공하는 Json string 배열 (선택)
  • fields : field 를 나열한 JSON 배열. 각각의 field 는 JOSN Object 이고 아래 요소들을 포함한다.
    • name : field 의 이름을 제공하는 JSON string (필수)
    • doc : field 를 사용자에게 설명하는 JSON string (선택)
    • type : 위에서 설명한 것과 같은 type
    • default : 이 field 의 기본 값. 해당 객체를 읽을때 만들어진 데이터에 field 가 없을때 Schema Evolution 의 목적으로 사용된다. default value 가 있다고 하더라도 field 를 optional 로 만들지는 않는다. 허용되는 값은 아래 표에 따라 field Schema 타입에 따라 다르다. Union 필드의 default 값은 Union 의 첫번째 값을 따른다. Bytes 와 Fixed 필드의 기본 값은 JSON string 이고 Unicode 포인트 0-255는 unsigned 8-bit byte value 0-255 로 맵핑된다. Avro 는 비록 데이터의 값이 default 와 동일하다고 하더라도 인코딩한다.
    • order : Field 가 어떻게 정렬될지를 특정한다 (선택). "ascending" (기본값), "descending", "ignore" 가 유효한 값이다. 

Enums

Enum 은 "enum" 타입 명으로 사용된다.

  • name : Enum 의 이름을 제공하는 JSON string (필수)
  • namespace : name 을 보충하는 JSON string
  • aliases : Enum 의 대체 이름을 제공하는 JSON string 배열 (선택)
  • doc : 사용자에게 schema 를 설명하는 문서를 제공하는 JSON string (선택)
  • symbols : symoble 을 나열하는 JSON 배열 (필수). 모든 Enum 의 symbols 은 유일해야한다. 모든 symoble 은 이 정규표현식에 매칭되어야한다. [A-Za-z_][A-Za-z0-9_]
  • default : Enum 타입에서 default 는 Writer Schema 로 작성한 데이터를 Reader Schema 가 읽을때 정의되어있지 않은 값을 마주쳤을때 사용한다 (선택). symbol 배열에 속해있는 Enum 이여야하고 JSON string 이다. 
{
  "type": "enum",
  "name": "Suit",
  "symbols" : ["SPADES", "HEARTS", "DIAMONDS", "CLUBS"]
}

Arrays

 Arrays 는 "array" 타입 명으로 사용된다.

  • items : 배열의 항목
{
  "type": "array",
  "items" : "string",
  "default": []
}

Unions

 Uninos 은 JSON 배열로 표현된다. ["null", "string"] 은 null 또는 string 일 수 이는 schema 를 정의한다. 

 

 default 값은 union 의 첫번째 타입과 매칭되어 사용된다는 것을 기억하자. 그래서 union 이 "null" 을 포함하고 있다면 "null" 이 리스트에 첫번째 요소가 되어야 null 이라는 값을 가질 수 있는 optional 필드로서 역할을 할 수 있다.

 

 Union 은 동일한 type 을 가질 수 없다. 예를들어 Union 은 2개의 배열 타입과 2개의 Map 타입을 가질 수 없다. 하지만 서로 다른 일므을 가진 두개의 타입은 허용된다. 

 

 Union 은 다른 union 을 바로 포함할 수 없다

Fixed

 Fixed 는 "fixed" 라는 타입 이름으로 사용된다

  • name : fixed 의 이름을 나타내는 string (필수)
  • namespace : name 을 보충하는 string
  • alias : 대체 이름을 제공하기 위한 JSON 배열 (선택)
  • doc : 사용자에게 schema 를 설명하는 문서를 제공하는 JSON string (선택)
  • size : 값 별 Bytes 의 수 (필수)
{"type": "fixed", "size": 16, "name": "md5"}

Names

 record, enum, fixed 는 Named 타입이다. name 과 namespace 로 나뉘는 두개의 파트를 가진 full name 을 갖는다. 이름의 동등성은 fullname 으로 정의된다.

 

 fullname 의 일부분인 name 은 아래 규칙을 따른다

  • [A-Za-z] 으로 시작
  • [A-Za-z0-9_] 만 포함

 namespace 는 각각의 이름을 . 으로 구분한다. empty string 은 사용하여 null namspace 를 가리킨다. fullname 과 name 의 동등성은 case-sensitive 하다.

 

null namespace 는 . 으로 구분된 name 배열을 사용하지 않는다. 따라서 namespace 의 문법은 아래와 같다.

 <empty> | <name>[(<dot><name>)*]

 

 record, enum, fixed 정의에서 fullname 은 아래의 방법으로 결정된다

  • name 과 namespace 는 둘 다 지정된다면. 예를들어 "name": "X", "namespace": "org.foo" 를 사용한다면 fullname 은 org.foo.X 가 된다
  • fullname 만 지정된다면. name 에 . 가 포함되어있다면 namespace 는 무시된다. "name": "org.foo.X" 의 fullname 은 "org.foo.X" 가 된다.
  • name 만 지정되고 name 에 . 이 포함되어있지 않다면. 이 경우에는 namespace 가 schema 혹은 protocol 로 둘러쌓여진다. 예를들어 "name": "X" 로 지정되고 "org.foo.Y" 에 정의되어있는 record 에서 사용되는 field 라면 fullname 은 "org.foo.X" 가 된다. 

 name 이 . 을 포함하고 있다면 fullname 으로 사용되고 name 이 . 을 포함하고 있지 않다면 namespace 는 schema 또는 protocol 로 부터 불러와지게 된다.

 

 Primitive 타입은 namespace 가 없고 Primitive 타입의 이름은 어떠한 namespace 에도 정의되어 있지 않다.

Aliases

 Named 타입과 Field 는 aliases 를 가질 수 있다. 구현체는 선택적으로 Alias 를 사용하여 Write Schema 를 Read Schema 에 맵핑할 수 있다. 이것은 Schema Evolution 와 Disparate 데이터 처리를 용이하게 한다.

 

 Alias 기능은 Reader Schema 의 Alias 를 사용하여 Write Schema 가 다시 데이터를 쓸 수 있다. 예를들어, Writer Schema 가 "Foo" 라는 이름으로 지었는데 Reader Schema 가 "Bar" 라는 이름으로 지었고 "Foo" 에 해당하는 Alias 가 있다면 구현체는 "Foo" 를 "Bar" 라는 것으로 읽을때 사용한다. 

 

Data Serialization and Deserialization

 Binary 로 인코딩된 Avro 데이터는 타입정보나 Field 이름을 포함하고 있지 않다. 그 결과로 직렬화된 데이터의 크기가 작다는 이점을 가질 수 있다. 하지만 Avro 데이터를 정확하게 읽어내기 위해서는 Schema 정보가 항상 사용되어야한다. 이것을 보장하기 위한 최선의 방법을 Read 할때 Schema 와 Write 할때 Schema 가 동일한 것을 사용하는 것이다.

 

 그래서 Avro 데이터를 저장할때는 Schema 정보도 함께 저장해야만 한다. Avro 기반 RPC 호출 시스템도 호출하는 측에서도 Schema 복사본을 사용하는 것이 보장되어야 한다. 새로운 Schema 로 데이터를 역직렬화 하는 것은 Schema Resolutioon 를 사용하면 성공할 수 있다.

 

 일반적으로 직렬화, 역직렬화 모두 깊이 우선 탐색, 좌에서 우로 Schema 탐색하면서 바주치는 primitive 타입을 직렬화/역직렬화 하면서 수행된다. 그래서 Avro 데이터를 Schema 로 읽는것은 데이터가 쓰여질 당시의 같은 Parsing Canonical Form Schema 가 아닐 수 있다. 이것이 동작하도록 하기 위하여 직렬화된 primitive 값이 역직렬화 Schema 와 호환이 가능해야한다. 예를들어, int 와 long 은 항상 같은 방법으로 직렬화된다. 따라서 int 는 long 으로 역직렬화 될 수 있다. 두 Schema 의 호환성은 데이터와 직렬화 포맷에 달려있기 때문에 동일한 Parsing Canonical Form 을 사용하는 것이 더 간단하고 안정적이다.

 

Encoding

 Avro 두개의 인코딩 직렬화를 지원한다. Binary 와 JSON. 대부분의 어플리케이션들은 Binary 인코딩이 더 빠르고 간단하기 때문에 Binary 인코딩을 사용한다. 그러나 Debugging 의 장점과 Web-based 어플리케이션들은 JSON 인코딩을 사용하는게 때때로는 적합할 수도 있다. 

 

Binary Encoding

  Binary 인코딩은 field 이름을 포함하지 않고 field, record 의 구분자 또한 저장하지 않는다. 그래서 Reader 는 데이터가 인코딩될때 Schema 에 의존한다.

 

Json Encodinng

 Union 을 제외하고 JSON 인코딩은 field default values 인코딩을 사용하는 것은 동일하다

 union 은 JSONT 으로 아래와 같이 인코딩된다.

  • 만약 타입이 null 이면 JSON null 로 인코딩된다
  • 그외에는 name/value 쌍으로 JSON 객체로 인코딩이 된다. name 은 type 의 name 이고 value 는 인코딩 되는 value 이다. Avro 의 Named type (record, fixed, enum) 은 유저가 특정한 이름이 name 으로 사용되고 다른 타입들은 type 의 name 이 사용된다.

예를 들어, union schema 인 ["null, "string", "Foo"]이 있고 Foo 가 Record 이름일때

  • null 은 null 
  • string "a" 는 {"string":"a"} 
  • Foo 객체는 {"Foo": {...}} 

로 직렬화 된다. 명심해야할 것은 Write 할 당시의 Schema 는 역직렬화를 위해서 필요하다. 한가지예로 JSON 인코딩이 int, long, float, double, record, maps, enum, string 등등을 구분하지 않는다.

Single-object encoding

Avro 로 직렬화된 데이터를 꽤 긴 기간동안 저장해야 하는 경우가 있다. 가장 흔한 예중의 하나는 Apache Kafka 에 몇주 동안 record 를 저장하는 경우이다. 

 

직렬화된 데이터가 저장된 이후에 Schema 가 변경된다면 이 시스템은 Read 할때와 Write 할때 다른 Schema 를 사용하게 된다. 따라서 Schema Evolution 을 정확하게 지원하기위하여 Write 할 당시의 Schema 를 알아야 하는 필요성이 있다. 대부분의 경우의 Schema 는 메시지에 스키마를 포함하기에는 너무 크기 때문에, Binary Wrapper 포맷이 이 유즈케이스를 효과적으로 지원한다.

 

Single object encoding specification

 Single Avro Object 는 아래와 같이 직렬화 된다. 

  • 2byte 마커 (C3 01) 가 Avro 메시지임을 나타내고 single-record format 을 사용하였음을 나타낸다
  • Object schema 를 식별하기 위한 CRC-64-AVRO fingerprint 의 little-edian 8byte 
  • Avro Binary 인코딩을 사용한 Avro serialized data

구현체는 2-byte 마커를 사용하여 payload 가 Avro 인지를 확인한다. 이것은 데이터가 Avro 로 직렬화되지 않았을 경우에 비싼 lookup 작업을 피할 수 있도록 돕는다. 

 

Schema Resolution

 RPC 또는 File 이던지 간에 Avro 데이터의 Reader 는 Write 할 당시의 Schema 가 데이터와 함께 제공되기 때문에 항상 데이터를 파싱할 수 있다. 하지만 Reader 는 다른 Schema 로도 데이터를 읽을 수 있도록 개발되었다. 예를들어 서로 다른 버전의 Software 로 Avro 데이터가 작성되었다면 Field 가 추가되거나 제거될 수 있다. 이 장에서는 이러한 Schema 의 차이를 어떻게 해결할 수 있는지를 설명한다.

 

데이터를 Write 할때 사용된 Schema 를 writer's schema 라 부르고 Read 할때 사용되는 Schema 를 reader's schema 라 부른다. 이 두 Schema 의 차이는 아래와 같이 해결될 수 있다

  • 만약 두 Schema 가 일치하지 않는다면 에러이다. 일치하게 하기 위하여 아래 규칙들이 지켜져야한다
    • 두 스키마의 Array item type 은 일치해야한다
    • 두 스키마의 Map item type 은 일치해야한다.
    • 두 스키마의 Enum name 은 일치해야한다.
    • 두 스키마의 Fixed 사이즈와 이름은 일치해야한다.
    • 두 스키마의 Record 이름은 일치해야한다.
    • 두 스키마는 같은 Primitive Type 을 가져야한다
    • Reader Schema 는 Writer Schema 로 부터 아래와 같이 변경될 수 있다
      • int는 long, float, double 로 변경될 수 있다
      • long 은  float, double 로 변경될 수 있다
      • float 은 double 로 변경될 수 있다
      • string 은 bytes 로 변경될 수 있다
      • bytes 는 string 으로 변경될 수 있다
    • 둘 다 record 인 경우
      • field 의 순서가 달라야한다. 필드는 이름에 의해서 매칭된다
      • 두 Schema 에서 같은 이름을 사용하는 field 는 재귀적으로 해결된다
      • Writer record 가 Reader record 에 없는 field 를 가지고 있다면, Writer 의 필드는 무시된다
      • Reader record 의 field 가 default value 를 가지고 있고 Writer Schema 가 해당 필드와 같은 이름을 가지고 있지 않다면 Reader 는 default value 를 사용한다
      • Reader record 가 default value 를 가지고 있지 않고 Writer Schema 가 동일한 이름의 field 를 가지고 있지 않다면 Error 가 발생한다.
    • 둘 다 Enum 인 경우
      • Writer symbol 이 Reader symbol 에 존재하지 않고 Reader 가 default value 를 가지고 있으면 default value 가 사용되고 default value 가 없는 경우 에러가 발생한다
    • 둘 다 Array 인 경우
      • Reader 와 Writer 아이템의 Schema 를 재귀적으로 해결한다
    • 둘 다 Map 인 경우
      • Reader 와 Writer 아이템의 Schema 를 재귀적으로 해결한다

 

'Avro' 카테고리의 다른 글

Avro - Serializer/Deserializer 비교  (0) 2022.02.18
Avro 시작하기 - Java, Gradle  (0) 2022.02.17
Apache Avro 소개  (0) 2022.02.17
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함