본문 바로가기

Hadoop

[Apache Avro] Avro 구조와 사용법

Apache Avro는 serialization library이다.

Avro 특징

특징

  1. json 형식으로 스키마를 지정하고 이 스키마 파일을 통해 serialize/deserialize 를 진행한다.
  2. java의 경우 플러그인을 통해 json 타입의 avro 스키마 파일을 읽고 .java 클래스를 생성할 수 있다.

장점

  1. 타 포멧 대비(ex. json) 데이터 사이즈가 컴팩트하다.
  2. Programing language에 상관없이 serialize/deserialize 가능하다.(자바에서 serialize 된 후 python에서 deserialize 가능)
  3. 다양한 데이터 타입을 지원한다.
  4. 스키마 변경을 유연하게 처리할 수 있다(Schema Evolution)

단점

  1. binary 형태로 저장시 디버깅이 어렵다.

Java Example With Maven

공식 홈페이지 기반의 예제를 만들어보았다.

<!-- maven pom.xml -->
<dependencies>
        <dependency>
            <groupId>org.apache.avro</groupId>
            <artifactId>avro</artifactId>
            <version>1.11.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.avro</groupId>
                <artifactId>avro-maven-plugin</artifactId>
                <version>1.11.0</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>schema</goal>
                        </goals>
                        <configuration>
                            <sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>
                            <outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

이후 src/main/avro 디렉토리를 생성후 avro 스키마 파일을 생성한다.

//user.avsc 파일 내용
{"namespace": "com.tistory.louisdev",
 "type": "record",
 "name": "User",
 "fields": [
     {"name": "name", "type": "string"},
     {"name": "favorite_number",  "type": ["int", "null"]},
     {"name": "favorite_color", "type": ["string", "null"]}
 ]
}
  • namespace: name을 구분짓기 위한 namespace, Java의 경우는 namespace가 Java package로 변경된다.
  • type: avro type(링크)
  • name: 데이터 record 이름, Java의 경우 클래스명이 된다.
  • fields: record를 구성하는 필드들

avro 스키마를 정의한 뒤 maven compile을 수행하면 avsc 파일을 읽어 java class를 생성해준다.

mvn clean compile

이후 User 클래스가 자동 생성된 것을 확인할 수 있다.

Avro Serialize

package com.tistory.louisdev;

import org.apache.avro.file.DataFileWriter;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.specific.SpecificDatumWriter;

import java.io.File;
import java.io.IOException;

public class AvroSerialize {
    public static void main(String[] args) throws IOException {

        //생성된 User 클래스를 통해 인스턴스 생성
        User user1 = new User();
        user1.setName("Alyssa");
        user1.setFavoriteNumber(256);

        User user2 = new User("Ben", 7, "red");

        User user3 = User.newBuilder()
                .setName("Charlie")
                .setFavoriteColor("blue")
                .setFavoriteNumber(null)
                .build();

        DatumWriter<User> userDatumWriter = new SpecificDatumWriter<>(User.class);
        DataFileWriter<User> dataFileWriter = new DataFileWriter(userDatumWriter);
        dataFileWriter.create(user1.getSchema(), new File("users.avro"));  //프로젝트 루트 디렉토리에 users.avro 파일에 serialized binary data를 저장한다.
        dataFileWriter.append(user1);
        dataFileWriter.append(user2);
        dataFileWriter.append(user3);
        dataFileWriter.close();

    }
}

Avro Deserialize

Serialize 단계를 거쳐 생성된 users.avro 파일을 읽어 User 인스턴스로 deserialize 한다.

package com.tistory.louisdev;

import org.apache.avro.file.DataFileReader;
import org.apache.avro.io.DatumReader;
import org.apache.avro.specific.SpecificDatumReader;

import java.io.File;
import java.io.IOException;

public class AvroDeserialize {
    public static void main(String[] args) throws IOException {
        DatumReader<User> userDatumReader = new SpecificDatumReader<>(User.class);
        DataFileReader<User> dataFileReader = new DataFileReader<User>(new File("users.avro"), userDatumReader);
        User user = null;
        while (dataFileReader.hasNext()) {
            user = dataFileReader.next(user);
            System.out.println(user);
        }

    }
}

수행결과

avro deserialize 결과

Avro File Structure

위 예제에서 serialize를 수행하면 users.avro 파일이 생성되는데, 이 avro 파일을 구조가 어떻게 구성되는지 확인해보자.

avro file structure

  • Header: record의 메타 정보가 저장된다.
    • 헤더의 시작 4 바이트는 "O", "b", "j", 1로 구성된다.
    • 이후 avro 스키마(user.avsc 파일로 저장한 스키마 정보)와 avro codec 정보가 저장된다.
    • 16-byte sync marker
  • Data Block: 실제 데이터 저장
    • 블록안의 오브젝트 갯수
    • 블록안의 serialize된 byte 수
    • 압축된 직렬화 오브젝트
    • 16-byte sync marker

 

Avro의 sync marker 역할

Avro File Structure를 보면 헤더와 블록사이, 블록과 블록사이 16-byte sync marker를 항상 생성하는것을 확인할수 있다.
이는 하둡이 블록단위로 데이터가 처리되는것과 관련이 있다.

hdfs에 저장된 파일은 여러 MR task에서 블록단위로 읽고 처리되는데, 이때 각 task들은 블록의 시작과 끝을 알아야 온전히 완전한 블록을 각각 읽고 처리할수 있게 된다. sync marker는 블록과 블록의 구분짓는 역할을 수행하여 MR task가 블록단위로 파일을 읽고 처리할수 있도록 한다. (참고)

 


참고문서

  1. avro 공식 가이드
  2. 위키 백과
  3. Avro Wiki FAQ

'Hadoop' 카테고리의 다른 글

[Hadoop] Active Namenode 구하는 법  (0) 2020.07.16