Apache ArrowのPMC(Project Management Commitee、プロジェクト管理チームみたいな感じ)のメンバーの須藤です。
みなさんはApache Arrowを知っていますか?Apache Arrowは数年後にはデータ処理界隈で重要なコンポーネントになっているだろうプロジェクトです。データ処理界隈に興味がある人は知っておくと役に立つはずなので1年ほど前にApache Arrowの最新情報(2018年9月版)をまとめました。この1年ですごくよくなったので2019年9月現在の最新情報を紹介します。
私は、PMCの中では唯一の日本人1で、コミット数は2番目に多い2ので、日本ではApache Arrowのことをだいぶ知っている方なはずです。Apache Arrowの情報は日本語ではあまりないので日本語で紹介します。
ちなみに、英語ではいろいろ情報があります。有用な情報源はApache Arrowの公式ブログや公式メーリングリストやそれぞれの開発者のブログ・発表などです。開発者のブログの中ではUrsa Labs Blogの隔月の開発レポートがオススメです。Ursa Labsはスポンサーを集めてフルタイムでApache Arrowの開発をしている非営利の組織です。(という説明でそんなに間違っていないはず。)
この記事ではそれらの情報へのリンクも示しながら最新情報を紹介するので、ぜひ英語の情報も活用してください。
Apache Arrowが実現すること
Apache Arrowが実現することは1年前と変わっていないので、Apache Arrowの必要性から知りたいという方はApache Arrowの最新情報(2018年9月版)を参照してください。まとめると次の通りです。
Apache Arrowは効率的に大量のデータをメモリー上で処理することを目指しています。そのためにしていることは次の通りです。
-
データ交換・高速処理しやすいApache Arrowフォーマットの仕様を定義
-
各種言語用のApache Arrowフォーマットを読み書きするライブラリーを開発
-
大量のメモリー上のデータを高速処理するためライブラリーを開発
Apache Arrowが向いている用途は次の通りです。
-
大量データの交換
-
メモリー上での大量データの分析処理
Apache Arrowの現状
それでは、2019年9月現在、Apache Arrowのデータフォーマットの仕様と実装がどのようになっているかを説明します。
データフォーマットの仕様
データフォーマットの仕様は去年中に固められるといいねと進んでいましたが、まだ固まっていません。しかし、今年中には固まるはずです。
現時点での最新リリースは0.14.1です。10月中に0.15.0がリリースされる予定です。順調にいけば、0.15.0の次が1.0.0になります。
1.0.0をリリースする段階でデータフォーマットの仕様が固まります。固まるというのはどういうことかというと互換性が保証されるということです。
互換性には後方互換性と前方互換性があります。
後方互換性が保証されるというのは新しい実装では新しい仕様のデータだけでなく古い仕様のデータも読めるということです。
前方互換性が保証されるというのは古い実装でも新しい仕様のデータを読めるということです。もし読めなくても読めないことがわかります。中途半端に読めるとか壊れたデータとして読めるということにはなりません。
データフォーマットの互換性は大事なので、今後、仕様を変更するときは、少なくともC++とJavaで実装し、それぞれで相互に正しくやりとりできるかを確認するテストを追加しなければいけなくなりました。そのため、C++でだけ実装しやすいがJavaでは実装しづらいような仕様は入りにくくなります。少なくともC++とJavaとなっているのは今のところC++とJavaの実装が進んでいるからです。今後、他の言語での実装が進んできたらC++とJava以外も候補に入ってくるかもしれません。
参考:[DISCUSS] Format changes: process and requirements
これまで、Apache Arrowフォーマットのデータはストレージに保存するのには向いていないという扱いでした。しかし、仕様が固まったら(1.0.0がリリースされたら)データが失われる心配をせずにストレージに保存することができます。
ただし、Apache Arrowフォーマットよりもストレージ保存に向いたフォーマットはたくさんある(たとえばApache Parquet)ので、ストレージに保存したい場合は、本当にApache Arrowフォーマットが適切なのかを考えてからどのフォーマットにするかを決めてください。たとえば、ストレージにはすごく空きがあるからデータの読み込みをすごく速くしたいということであればApache Arrowフォーマットを使ってもよいでしょうが、書き込むデータ量はApache Parquetなど他のストレージ向けのフォーマットよりも大きくなりがちなので書き込み速度はそこそこ(I/Oネック)になるかもしれません。
データフォーマットのバージョン
1.0.0がリリースされたらデータフォーマットのバージョン付けにはセマンティックバージョニングを採用する予定です。つまり、互換性が壊れるような変更がなければメジャーバージョンは同じまま(1.X.Yのまま)になります。互換性がある仕様変更の場合はマイナーバージョンが上がります。1.0.0のあとに互換性のある仕様変更があったら1.1.0になります。今のところ、パッチバージョンは使う予定はありません。
今までは明示的にデータフォーマットにバージョンを振っていませんでした。振っていませんでしたが、メタデータバージョンというものがありました。これは今のところV1, V2, V3, V4があります。データフォーマットのメジャーバージョンが上がったらここに新しい値が増えます。2.0.0になったらV5が増えます。
少し紛らわしいかもしれませんが、1.0.0以降は「ライブラリーのバージョン」と「データフォーマットのバージョン」は別々に採番されます。ライブラリーのバージョンは2.0.0でデータフォーマットのバージョンは1.0.0というような状況になります。
参考:[VOTE] Adopt FORMAT and LIBRARY SemVer-based version schemes for Arrow 1.0.0
ライブラリーのバージョン
1.0.0からはライブラリーのバージョンもセマンティックバージョニングに従います。つまり、互換性が壊れるような変更があればメジャーバージョンが上がるということです。
現時点ではリリースするたびに互換性が壊れる可能性があるので毎回メジャーバージョンが上がる予定です。2ヶ月くらいごとにリリースする予定なので2ヶ月くらいに1回メジャーバージョンが上がる予定です。
ライブラリーのメジャーバージョンは頻繁に上がりますが、データフォーマットのメジャーバージョンはそんなに上がらないはずです。そのため、次のようにライブラリーのバージョンとデータフォーマットのバージョンはズレます。
-
1.0.0の次のリリース
-
ライブラリーのバージョン:2.0.0
-
データフォーマットのバージョン:1.0.0
-
-
1.0.0の次の次のリリース
-
ライブラリーのバージョン:3.0.0
-
データフォーマットのバージョン:1.0.0
-
Apache Arrowにはたくさんのプログラミング言語向けのライブラリーがありますが、すべてこのルールで一緒にリリースされます。
扱えるデータ
バージョンの話はこれくらいにして、Apache Arrowが扱えるデータについて説明します。基本的なデータについてはApache Arrowの最新情報(2018年9月版)を参照してください。ここではこの1年で新しく扱えるようになったデータについて説明します。
新しく次のデータを扱えるようになりました。
-
2つの日の差(64bit整数)
- 何日離れているか
-
2つの月の差(64bit整数)
- 何ヶ月離れているか
-
2つの時刻の差(64bit整数)
- サポートしている単位:秒・ミリ秒・マイクロ秒・ナノ秒
-
2GiB以上の大きさの可変長バイナリーデータ
-
2GiB以上の大きさの可変長UTF-8文字列
-
2G個以上の要素を扱えるリスト
-
疎な多次元配列
また、ユーザーが独自に型を追加できるようになりました。これを拡張型と呼びます。
拡張型は次のように実現しています。
-
生データはApache Arrowが提供するプリミティブ型を使って表現する
-
生データにメタデータを付与して追加情報を表現する
たとえば、UUID型は次のように実現します。
-
生データは16バイトの固定長バイナリーデータ型を使って表現する
-
生データに次のメタデータを付与
-
拡張型名:uuid
-
UUIDのバージョン:1
-
この実現方法のメリットは、使っているApache Arrowのライブラリーが対象の拡張型のことを知らなくても(UUID型について知らなくても)データを扱えるという点です。対象の拡張型を知らなかったら単に生データ(UUID型ならただの16バイトの固定長バイナリーデータ)として扱えばよいからです。もちろん、その拡張型固有の操作はできませんが、要素数を数えたり、複数のデータチャンクをまとめて次のデータ処理モジュールにデータを渡すといった基本的な操作はできます。
データ処理
Apache Arrowは各種データ処理ツールが共通で使える高速なデータ処理機能の開発も重視しています。去年の時点ではデータフォーマットの方に注力していたためあまり進んでいませんでした。しかし、この1年でデータ処理部分の実装が進んでいます。
特に進んだのがC++とRustです。C++のバインディングとして実装されているPython、R、Rubyもその恩恵を受けています。
それではそれぞれの言語毎にデータ処理部分がこの1年でどう改良されたかを紹介します。去年の時点で実装されている処理についてはApache Arrowの最新情報(2018年9月版)を参照してください。
C++
C++でこの1年で実装された処理は次の通りです。
-
指定した値と各要素の比較
-
結果:真偽値の配列
-
比較方法:
==
,!=
,<
,<=
,>
,>=
-
例:
Compare([null, 1, 2, 3], 2, >=)
→[null, false, true, true]
-
-
非NULLの要素数のカウント
-
結果:要素数(数値)
-
例:
Count([null, 1, 2, 3])
→3
-
-
真偽値配列による要素の選択
-
結果:対応する真偽値配列の要素が偽ではない要素の部分だけが残った配列
-
例:
Filter([null, 1, 2, 3], [true, false, null, true])
→[null, null, 3]
-
-
値ごとの要素数のカウント
-
結果:構造体(
{"Values": 入力の配列と同じ型, "Counts": 64ビット整数}
)の配列 -
例:
ValueCounts([null, "a", "b", "a"])
→[{"Value": "a", "Counts": 2}, {"Value": "b", "Counts": 1}]
-
-
対象の配列の中に指定した配列にある要素が含まれているか
-
結果:真偽値の配列
-
例:
IsIn([null, 1, 2, 3], [1, 3])
→[null, true, false, true]
-
-
算術平均(NULLを無視)
-
結果:平均値(64bit浮動小数点数)
-
例:
Mean([null, 1, 2, 3])
→2.0
-
-
ソートし、ソート後の各要素がソート前は何番目の要素だったかを返す(NULLは最後になる)
-
結果:64bit整数の配列
-
例:
SortToIndices([null, 3, 1, 2])
→[2, 3, 1, 0]
-
-
合計(NULLを無視)
-
結果:合計値(64bit整数または64bit非負整数または64bit浮動小数点数)
-
例:
Sum([null, 1, -2, 3])
→2
-
-
配列から指定したインデックスの要素を抽出
-
結果:対応する要素だけが残った配列
-
例:
Take([null, "a", "b", "c"], [2, null, 1])
→["b", null, "a"]
-
さらにGandivaという名前の式コンパイラーが取り込まれました。Apache Arrowの最新情報(2018年9月版)の段階で取り込まれようとしていたものが正式に取り込まれたということです。
前述の処理は配列単位の処理ですが、Gandivaはレコードバッチ(レコードの集合)に対する処理を扱います。たとえば、「record.column1 > 100 && record.column2 == "XXX"
」のような処理を扱います。このような処理をLLVMを使って実行時にネイティブコードにコンパイルしてから実行するので高速に式を評価できます。
Gandivaは単に条件で絞り込むような簡単な式だけでなく、SQLで記述できるような集約処理や条件分岐(CASE
相当)などの複雑な式も扱います。
参考:Gandiva: A LLVM-based Analytical Expression Compiler for Apache Arrow | Apache Arrow
現時点でGandivaはC++だけでなくJava、Python、Rubyから使えます。
現時点では前述の配列単位の処理(内部では計算カーネルと呼んでいます)とGandivaは特に連携していません。算術演算のような基本的な処理はどちらにもあります。では、どうやって使い分けるのかが気になりますね。おそらく、次のような使い分けになるでしょう。
-
中間結果(最終結果じゃない)のレコードバッチの処理:Gandiva
- 例:レコードのフィルター
-
最終結果を生成する処理:計算カーネル
- 例:合計値の計算
参考:Compute kernels and Gandiva operators
もう少し先になると、ユーザーはもう少し高レベルのAPIを使うことができるようになるはずです。どこかからデータを読み込み、読み込みながら指定したクエリーでデータを絞り込めるようなAPIです。
データ読み込み処理はデータセットAPIとして実装が始まっています。
今のところローカルにあるApache Parquetフォーマットのデータだけ読み込めますが、今後、CSVやJSONフォーマットのデータも読み込めるようになったり、S3上のファイルなどリモートにあるデータも読み込めるようになる予定です。条件のプッシュダウン(指定した条件のデータだけ読み込む)も対応します。プッシュダウンなしで、読み込んだ後に不要なデータを捨てる方法もありますが、それよりも、そもそも読まない方が圧倒的に高速なのです。
参考:
読み込んだデータはクエリーエンジンで処理できるようになる予定です。クエリーエンジンは内部で計算カーネルやGandivaを利用して効率的に処理します。
参考:
ユーザーがより扱いやすいAPIとしてデータフレームAPIも実装する予定です。
参考:
楽しみですね!
Rust
Rustでこの1年で実装された処理は次の通りです。
-
要素ごとの算術演算(SIMD対応)
-
要素ごとの論理演算(AND・OR・NOT)
-
要素ごとの比較(SIMD対応)
-
キャスト(型の変換)
-
配列から指定したインデックスの要素を抽出
-
時刻配列の各要素を時間(hour)に変換
Rustでもクエリーエンジンの実装が始まりました。DataFusionという名前です。
C++のクエリーエンジンはSQLなど特定のフロントエンドを用意しない3設計4ですが、DataFusionにはSQLのフロントエンドがあります。
まだ簡単なSQLしか処理しかできませんが、より複雑なSQLも処理できるように開発を進めています。
参考:DataFusion: A Rust-native Query Engine for Apache Arrow | Apache Arrow
この1年でRustのApache Parquet実装がApache Arrowに取り込まれたので、Apache Parquetフォーマットのデータもデータソースとして扱えます。
現時点ではすべてRustで実装していますが、どうにかしてGandivaとつなげられないかという検討もしています。
参考:[DISCUSS] [Gandiva] Adding query plan to Gandiva protobuf definition
DataFusionの開発をしている@andygrove73はDataFusionベースの分散計算プラットフォームBallistaの開発をはじめました。PoCのプロジェクトだそうなので、ここでの実験の成果がDataFusionに還元されていくのだと思います。
データ処理のまとめ
データ処理まわりについては、この1年で特に進んだC++実装とRust実装を紹介しました。
Arrow Flight - 高速RPCシステム
この1年Arrow FlightというApache Arrowベースの高速RPCシステムの開発が始まりました。gRPCベースのプロトコルですが、できるだけ高速になるように設計されています。たとえば、データのコピーができるだけ少なくなるようになっています。
ここでもう少しArrow Flightについて紹介できればよかったのですが、私がまだ触っていないのでそんなに説明できることがありません。残念。
Java実装を主に実装したDremioの人たちが書いたArrow Flightの記事Understanding Apache Arrow Flightもあるのですが、ちょっとこれだけだとピンとこないと思います。
現時点でArrow Flightを使えるのはC++とJavaとPythonです。
来年にはRubyでも使えるようにして紹介できるようにしたいな。
追記:Apache Arrow Flightの日本語情報を公開
各言語での実装の完成度
各言語での実装の完成度もApache Arrowの最新情報(2018年9月版)からの差分を紹介します。
まず、次の言語が増えました。
-
C#
-
R
-
MATLAB
C#はネイティブ実装で、RとMATLABはC++実装のバインディングです。
C#実装は基本的な型にだいたい対応しています。前述の新しく扱えるようになったデータはまだ対応していません。NuGetでインストールできます。
参考:NuGet Gallery | Apache.Arrow
R実装はかなりC++実装をカバーしています。Python実装やRuby実装ほどではありませんが、完成度が高めです。Apache Parquetの読み込みもサポートしました。RからApache Parquetを読み込めるとうれしい人が多いはずです。CRANからインストールできます。
MATLAB実装はFeatherフォーマットを読み書きできるだけです。今後、開発が活発になるかどうかはよくわかりません。
Apache Arrow利用者
Apache Arrowの利用者が増えています。
TensorFlowはApahche Arrowフォーマットのデータをデータセットとして使えるようにしました。
参考:TensorFlow with Apache Arrow Datasets
BigQuery Storage APIもApache Arrowフォーマットのデータをサポートしました。
参考:
PG-StromというPostgreSQLの拡張モジュールもApache Arrowフォーマットのデータをサポートしました。PG-StromはGPUを使って高速にデータ処理するための拡張モジュールです。PG-Stromの関連モジュールとしてArrow_Fdwがあり、これを使うとストレージに置いてあるApache ArrowフォーマットのデータをPostgreSQLから読み込めます。読み込んだデータはPG-Stromで高速に処理できます。
参考:PostgreSQLをどこまで高速化できるのか?〜ハードウェアの限界に挑むPG-Stromの挑戦〜
PG-StromにはPostgreSQLに接続して結果をApache Arrowフォーマットのデータとして保存するpg2arrowというコマンドもあります。この機能はC++のデータセットAPIで実現しようとしている機能の1つでもあります。この機能をApache Arrow本体に取り込まないか?という話もあるので楽しみですね。
参考:Contributing to Apache Arrow? · Issue #9 · heterodb/pg2arrow
1.0.0がリリースされたらもっとApache Arrowの利用者が増えるでしょう。
Apache Arrowの今後
ここまででApache Arrowのこの1年の最新情報を説明しました。最後に今後のことを説明します。
1.0.0リリース
前日の通り、近いうちに1.0.0がリリースされます。
去年、Apache Arrow東京ミートアップ2018というApache Arrowイベントを開催しましたが、1.0.0に合わせて今年もApache Arrowイベントを開催する予定です。楽しみにしていてください!
Apache Arrow利用者の増加
1.0.0がリリースされるとこれまで「様子見」だった人たちが使うようになるはずです。そうするといろんなところでApache Arrowを利用した高速なデータ交換ができるようになります。
次の1年でApache Arrow対応プロダクト・サービスはたくさん増えるでしょう。
データ処理機能の拡充
前述の通りC++実装ではデータセット・クエリーエンジン・データフレームなどデータ処理機能の実装に注力していきます。次の1年で使える機能がかなり増えるでしょう。
まとめ
2019年9月時点のApache Arrowの最新情報を、2018年9月からの差分という形でまとめました。Apache Arrowは数年後にはデータ処理界隈で重要なコンポーネントになっているだろうプロジェクトです。日本でもApache Arrowのことを知っている人が増えるといいと思うので日本語でまとめました。Apache Arrowを使う人が増えるといいなぁと思います。さらに言えば開発に参加する人も増えるといいなぁと思います。
私が知っていることはまとめたつもりですが、もしかしたらカバーできていない話があるかもしれません。もし、「○○についても知りたい!」という方がいたらApache Arrowのことを日本語で話せるチャットで声をかけてください。この記事に追加します。
Apache Arrowについて講演して欲しいという方はお問い合わせフォームからご連絡ください。
私はデータ処理ツールの開発という仕事をしたいと思っています。その中にはもちろんApache Arrowの開発も含まれています。一緒に仕事をしたい!(自社サービスをApache Arrow対応したいとか)という方はお問い合わせフォームからご連絡ください。