Apache Avroについて調べてみました。よくわからないところも多いので、指摘などがあればぜひコメントお願いします。
1. 概要
Avroは、スキーマ定義とバイナリでのエンコーディングを特徴とする、システム間でデータ交換を行うためのフォーマット。JSONでは型がなく文字列ベースで効率が悪くて困るような場合に使うもの。似たようなものとしては、Protocol BuffersやMessage Packなどがある。
主に、ファイル形式、RPC用のプロトコル定義、Kafkaなどのメッセージ形式として使われる。以下では、RPCに関しては(よくわからないので)触れない。
日本語では、Apache Avro vs Protocol Buffersの資料がAvroの概要とProtocol Buffersとの違いがまとまっていてわかりやすい。
C, C++, C#, Java, PHP, Python, Ruby については公式のライブラリが用意されているが、(特に新し目の機能は)言語によって対応度合いが若干違うように見える。Goは、linkedin/goavroがよく使われていると思われる。以下の説明はJavaをベースにしている。
2. 主要な機能
2.1 スキーマ定義
スキーマはavscファイルで定義する。以下のデータ型が用意されていて、組み合わせてフィールドを定義する。若干古いが、Oracle NoSQL Databaseスタート・ガイドの第7章 Avroスキーマに日本語のドキュメントがあるので参考になる。
種類 | データ型 |
---|---|
Primitive Types | null, boolean, int, long, float, double, bytes, string |
Complex Types | record, enum, array, maps, union, fixed |
Logical Types | Decimal, Date, Time, Timestamp, Duration |
Logical Typeは、物理形式としてはPrimitive Typeを使ってシリアライズされる派生型。
スキーマは、JSON形式で記述する以外に、IDLから作成する方法もある。IDLを使うと、importなどの機能を使うことができる。
2.1.1 スキーマの例
(http://avro.apache.org/docs/current/spec.html より)
{"namespace": "example.avro",
"type": "record",
"name": "User",
"fields": [
{"name": "name", "type": "string"},
{"name": "favorite_number", "type": ["int", "null"]},
{"name": "favorite_color", "type": ["string", "null"]}
]
2.1.2 IDLによるスキーマ定義の例
(https://mingqin.wordpress.com/2015/01/30/avro-idl-composites-avro-schemas-by-avrohubtool/ より)
@namespace("avro.examples.baseball")
protocol Baseball {
import schema "name.avsc";
import schema "coache.avsc";
import schema "position.avsc";
import schema "player.avsc";
record Team {
Player player;
Coache coache;
}
}
2.1.3 Schema evolution
Avroでは、互換性のルールがあり、スキーマの変更時に互換性を維持できるようになっている。ただ、公式の仕様はわかりにくいので、viniciusccarvalho/schema-evolution-samplesの説明・例や、Confluent Platformの各コンポーネントの特徴 のほうがわかりやすい。
|互換性の種類|説明|
|---|---|---|
|前方互換|新しいスキーマで作られたデータを古いスキーマで読む|
|後方互換|古いスキーマで作成されたデータを新しいスキーマで読む|
前方互換性を維持するためには、スキーマへのカラム追加時にデフォルト値を指定する必要がある。
2.2 データ形式
Avroで用意されているデータ形式は3種類あり、用途に合わせて選択する。データの読み書きにはスキーマが必要になるため、ヘッダによるオーバーヘッドとスキーマの運用を考慮して決める必要がある。
種類 | 内容 | 説明 | スキーマの運用 |
---|---|---|---|
Binary Encoding | バイナリのデータのみ、ヘッダなし | BinaryEncoderを使う。単純なエンコーディングとしてのみ使用する場合 | ディレクトリやトピックとスキーマを規約で対応付けておく。スキーマ更新時は全体で同期をとって対応が必要 |
Object Container Files | ヘッダにスキーマ定義を含む | DataFileWriterを使う。ファイルでのデータ交換用 | 埋め込まれているスキーマをそのまま利用可能 |
Single-Object Encoding | ヘッダにスキーマのフィンガープリントを含む | BinaryMessageEncoderを使う。Kafkaなどのメッセージング用 | フィンガープリントを使用してスキーマを解決する |
Object Container Files形式は、deflate/snappy圧縮も可能。また、BigQuery、RedShift、Sparkなどでサポートされている。Amazon EMR の Avro フォーマットのデータを Amazon Redshift にロードするも参照。
メッセージ用途には、Object Container Files形式はオーバーヘッドが大きいので、軽量な形式としてSingle-Object Encodingが用意されているが、自前でスキーマのバージョンなどを埋め込んで解決してもよい。
動的にスキーマを解決する仕組みとしては、大掛かりになるがConfluent Schema Registryもある。Confluent Platformの各コンポーネントの特徴も参照。
2.3 シリアライズに使用される主要なクラス
基本的には、DatumWriterのいずれかのサブクラスと、EncoderもしくはFileWriterを使用してシリアライズする。シリアライズのサンプルはGetting Started (Java)を参照。デシリアライズでは、対応するReader/Decoderを使用する。
こちらも若干古いが、Oracle NoSQL Databaseスタート・ガイドの第8章 Avroバインディングに日本語のドキュメントがある。
package | class/Interface | 説明 |
---|---|---|
org.apache.avro.io | DatumWriter(IF) | スキーマに従ってインスタンスをEncoderに書き出す |
org.apache.avro.io | SpecificDatumWriter | スキーマから自動生成されたクラスを使用してシリアライズ |
org.apache.avro.io | GenericDatumWriter | クラスの自動生成を使わずにシリアライズする |
org.apache.avro.io | ReflectDatumWriter | 既存のクラスをシリアライズする |
org.apache.avro.io | Encoder(IF) | フィールドの値をシリアライズする |
org.apache.avro.io | BinaryEncoder | フィールドの値をバイナリエンコーディングする |
org.apache.avro.file | DataFileWriter | DatumWriterをObject Container Files形式のファイルに出力する(encoder不要) |
org.apache.avro.message | MessageEncoder(IF) | ByteBuffer, OutputStreamに出力する |
org.apache.avro.message | BinaryMessageEncoder | Single Object EncodingでByteBuffer, OutputStreamに出力する |
PythonやRubyではクラス生成はサポートされていない(というか必要がない)ので、GenericDatumWriter相当のみとなる。Getting Started (Python)や、Avro in Rubyも参照。
Schema Evolutionを考えると、SpecificDatumWriterを使用する場合、自動生成されたクラスをどうバージョン管理するかが少し悩ましい気がする。
Appendix
- commercehub-oss/gradle-avro-plugin Gradleのplugin
- [AVRO-1891] JavaでLogical Typeを含むUnionができないというバグ。Nullを許可する場合にはunionを使うので、現状ではLogical TypeでNullを許可することができない。1.8.3で対応予定。
- GADGT: The futue of gogen-avro
- How to encode/decode Kafka messages using Avro binary encoder? - Stack Overflow