Bigqueryはしばらく前から、AVROフォーマットでデータをエクスポートできるようになっています。
AVROフォーマットは、Apacheプロジェクトの一つとして開発されているシリアライズフォーマットで、以下の様な特徴を持ちます。
- バイナリフォーマットでとてもコンパクト
- 型情報を持ったスキーマがある、スキーマはJSONで定義する
- JSONの様に入れ子が可能
- null可かどうかをスキーマで表現可能
- シリアライズして出力した後でも別のスキーマに変更できる
恐らく一番大きな特徴は型付きでバイナリにシリアライズしているのに、後からカラムを追加したり削除して読み出すことができる点です。
スキーマ情報はファイル内に埋め込まれていますが、別途定義したものと組み合わせて読み出すことができ、後からデフォルト値を追加したりできるようになっています。
また、Hadoopから直接データを読み出せるライブラリが揃っていて、Hadoopエコシステムと相性が良いという性質もあります。
JSONと比べた時の利点は、型がよりかっちり決まっている点と、バイナリフォーマットで末尾まで読まなくても随時パースしながらストリームで処理できる点です。
まあ、Bigqueryの場合はエクスポートの出力がlined JSONなので、その辺りはあんまり変わらないですが、パースの負荷は軽くて済みます。
さて、AVROはBigqueryのデータエクスポートには、JSONとAVROで大きな違いがあります。
BigqueryはJSONでエクスポートすると何でもかんでも文字列になって出力されます intとtimestampが文字列になって出力されます。
JSONだとこれが割と面倒で、そこそこのデータ量を取得して別の用途に利用しようとすると、無駄な型変換が必要になって処理負荷がかかります。
特に面倒なのが、ARRAY型をエクスポートした時で配列内部にあるデータも文字列になっているので、数値の配列が欲しい場合は一回全データを走査して数値に変換する必要があります。
AVROだとその辺りは、大体一致した型で出力されます。(時刻はlong型になります)
配列内部のデータもちゃんとイメージ通りの型で出力してくれるので、余計な変換をする手間が省けます。
私はBigqueryから出力したデータはembulkを利用して取得・加工を行うことが多いので、embulkからAVROフォーマットを扱えるようにembulk-parser-avroを作りました
in:
type: file
path_prefix: "items"
parser:
type: avro
avsc : "./item.avsc"
columns:
- {name: "id", type: "long"}
- {name: "code", type: "string"}
- {name: "name", type: "string"}
- {name: "description", type: "string"}
- {name: "flag", type: "boolean"}
- {name: "price", type: "long"}
- {name: "item_type", type: "string"}
- {name: "tags", type: "json"}
- {name: "options", type: "json"}
- {name: "spec", type: "json"}
- {name: "created_at", type: "timestamp", format: "%Y-%m-%dT%H:%M:%S%:z"}
- {name: "created_at_utc", type: "timestamp"}
out:
type: stdout
// item.avsc
{
"type" : "record",
"name" : "Item",
"namespace" : "example.avro",
"fields" : [
{"name": "id", "type": "int"},
{"name": "code", "type": "long"},
{"name": "name", "type": "string"},
{"name": "description", "type": ["string", "null"]},
{"name": "flag", "type": "boolean"},
{"name": "created_at", "type": "string"},
{"name": "created_at_utc", "type": "float"},
{"name": "price", "type": ["double", "null"]},
{"name": "spec", "type": {
"type": "record",
"name": "item_spec",
"fields" : [
{"name" : "key", "type" : "string"},
{"name" : "value", "type" : ["string", "null"]}
]}
},
{"name": "tags", "type": [{"type": "array", "items": "string"}, "null"]},
{"name": "options", "type": {"type": "map", "values": ["string", "null"]}},
{"name": "item_type", "type": {"name": "item_type_enum", "type": "enum", "symbols": ["D", "M"]}},
{"name": "dummy", "type": "null"}
]
}
こんな感じで利用できます。
Bigqueryからデータを出力した後で、それを加工する際の型変換が無駄だなーと思ってる方が居たら、使ってみてはどうでしょうか。
~