新春特別企画

2016ー2017のApache Sparkに起こったこと起こること

Sparkの2016年は、1月4日にバージョン1.6.0のリリースからスタートしましたが、なんといっても今年の目玉はバージョン2.0.0のリリースでした。2016年7月26日にリリースされ、この開発には2,000以上のパッチが投稿され、世界中から280人もの開発貢献者の参画があったと言われています。

バージョン2.0ではアーキテクチャが抜本的に見直され、さらなる性能向上、さらなる使いやすさが実現しました。両バージョンについては現在もメンテナンスが続けられており、本稿執筆時点では1.6.3、2.0.2が最新リリースです。そして、12月28日にバージョン2.1がリリースされました。本稿では、2016年後半の中心だったバージョン2.0の特徴について、バージョン1.6以前も振り返りつつおさらいし、2017年にどのような取り組みがなされるかについてお伝えします。

Sparkの特徴のおさらい

2016年のSparkの話に入る前に、Sparkのおさらいをしておきましょう。Sparkの特徴を「性能向上」「使いやすさ」の2軸で簡単に確認します。

まず「性能向上」についてですが、もともとHadoopでも利用されているMapReduceでは非効率になる処理の改善がSparkの開発の元々の目的でした。具体的には多段のMapReduceジョブを実行する際の効率化やキャッシュの利用により、余計なI/Oを減らすことで高速化を目指しました。これには、コモディティあるいはモダンなハードウェアの能力を「使い切る」ことで高速な処理をリーズナブルに実現したいというオープンソースソフトウェアの哲学が源流にあるといえます。

もう一点が「使いやすさ」です。MapReduceではまだ利用の敷居が高いと言われてきた並列分散処理のハードルを下げる仕掛けがSparkにはあります。大きく以下の4点があります。

  1. RDDと呼ばれるデータ構造の導入によるデータの抽象化と、データ処理を記述するのに理解しやすいAPIの整備
  2. 開発言語のサポート、特にデータ分析界隈で利用者が多いPython対応の開発が進む
  3. 用途別ライブラリの導入:SQL(Spark SQL⁠⁠、ストリーム処理(Spark Streaming⁠⁠、機械学習(MLlib⁠⁠、グラフ処理(GraphX)
  4. インタラクティブシェルの導入:データ分析の現場でニーズが高い試行錯誤をしやすく

Sparkが単に性能向上を目指す以外に、使いやすさも追求しているあたりが利用者の目線に立った開発が行われている大事なポイントだといえます。

2016年のApache Spark

Sparkがもつ「性能向上」⁠使いやすさ」という2つの特徴それぞれからSparkの進化を見てみます。

「性能向上」の面ではProject Tungstenと呼ばれる性能向上の取り組みがなされており、コモディティあるいはモダンなハードウェアの能力を使い切る活動がさらに推し進められています。

「使いやすさ」の面では、DataFrame/Datasetと呼ばれるデータ構造の導入により、並列分散処理利用時のハードルを下げる取り組みが行われています。以下、それぞれについて見ていきます。

Project Tungsten

Project Tungstenと呼ばれる「性能向上」の取り組みの背景には、ハードウェアの進化があります。2010年から2015年にかけてメモリの大容量化、SSDや10Gbpsネットワークのコモディティ化によりハードウェアの性能が向上し実質的に10倍程度向上したといえます。この結果、Spark開発当初はI/Oが性能面でのボトルネックになりがちでしたが、それがCPUに移りつつあります。これを、メモリの利用効率改善やモダンなCPU機能の活用によりCPUネックの解消を行っている活動がProject Tungstenです。

また、MPPデータベースに代表されるようなモダンな処理エンジンの良い点を見習い、そこで利用されている高速化のための取り組みをSparkで利用されるワークロードに適するよう導入が進められています。

バージョン1.6以前でもProject Tungstenの取り組みは行われていました。大きく3点あり、Java JVM利用によるGCの影響緩和のための独自のメモリ管理機構の導入、データがキャッシュに乗ることを意識したデータ構造やアルゴリズムの導入、Codegen(実行時コード生成)と呼ばれる、Spark SQLでクエリ記述した際のクエリ解析時に式を高速に評価するためのカスタムコードをコンパイル時に生成、といったものです。

バージョン2.0のProject Tungstenではバージョン1.6での活動を下地として、さらに2つの取り組みを行いました。

1つはWhole Stage Codegenと呼ばれるCodegenの後続となる取り組みです。入力データをスキャンし、フィルタし、射影をとって集約した結果を出力するといった処理を行うことはよくあると思いますが、このような複数のオペレータが存在する処理を行う際の高速化です。1レコード取り出すたびにオペレータの数分だけ都度メソッド呼び出しが発生していたのを、複数オペレータに対してまとめてカスタムコードを生成し、オペレータ間のレコード受け渡しに伴うメソッド呼び出しを減らしました。余分な処理を除去することでCPU利用効率のさらなる改善が期待できます。

もう1つはVectorizationです。Spark SQLにおいて入力ファイルフォーマットのParquetというカラム指向ファイルフォーマットを利用する際の高速化です。JVMのJITによる最適化を促すことで、ループ展開やSIMD命令によって、複数のレコードをまとめて処理する際の高速化を実現します。

DataFrame/Dataset

バージョン2.0のDataFrame/Datasetの話に入る前に、DataFrameの導入の背景についておさらいしておきます。DataFrameはスキーマを備えた、RDBMSにおけるテーブルのようなデータ構造です。DataFrameはいくつかの課題の解決を目的として導入されました。たとえば、もともとSparkで導入されていたRDDと呼ばれる配列のようなデータ構造に対して処理を記述した場合の見通しの悪さを改善するといった点が挙げられます。

さらに使いやすさの改善だけでなく、性能改善もあわせて行われており、DataFrameベースの処理は、オプティマイザにより最適化されるようになりました。また、アプリケーション開発言語間で存在していた処理の性能差をなくすように開発が進められたことで、DataFrameの利用が主流となるような礎を築きました。

バージョン1.6では、このDataFrameを発展させる形でDatasetと呼ばれるデータ構造を導入しました。Datasetもまたテーブルのようなデータ構造なのですが、データを直接Spark SQLの内部表現(UnsafeRow)に変換し、シリアライズ効率の改善を実現しています。ただ、バージョン1.6ではDataFrameとDatasetが似たようなデータ構造で併存する形となっており、アルファリリース扱いのままでした。

バージョン2.0のDataFrame/Datasetは、Datasetの開発をさらに推し進め、併存する形となっていた両者の内部アーキテクチャをDatasetベースに整理することで、利用側から見たときのSpark全体としてのAPIの統一を図りました。これにより、Scala言語ではDataFrameはDatasetの型付き表現という扱いになりました。また、バージョン1.6のDataFrameではできなかった列名や型の誤りをコンパイル時に検出できるようになったことで、利用者にとっての利便性が高まりました。

2017年のApache Spark

次に、2017年のApache Sparkの開発がどのように進められるか、現時点でわかっている情報でお伝えします。ここでも「性能向上」に関してはProject Tungstenの後続の取り組み、⁠使いやすさ」に関しては「DataFrame/Dataset」の後続の取り組みに着目します。

Project Tungsten

Project Tungstenの「性能向上」の取り組みはさらに続きます。まずはコストベースオプティマイザ(CBO)の導入です。リレーショナルデータベースなどでは一般的な機能ですが、Sparkの開発においても統計情報収集のフレームワークの導入と合わせて検討されています。

続いて、メニーコア向けの最適化です。特に、Sparkの各プロセスにおいて処理されたデータをプロセス間で交換するシャッフルと呼ばれる処理を行う際、同一計算機内に存在しているプロセス間の通信を効率化することが検討されています。

最後にCodegenです。Codegenによって生成されるコードの品質向上の取り組みが検討されています。DataFrameをインメモリカラムナフォーマットとしてキャッシュできるようになっていますが、それに最適化されたコードを生成する取り組みが進められています。

DataFrame/Dataset

「使いやすさ」向上の取り組みに関しては、昨今のSpark上のライブラリの開発トレンドとして、DataFrame/Datasetベースのライブラリが増えてきたことが挙げられます。機械学習ライブラリであるMLlibでいち早くDataFrameに対応した開発が行われており、spark.mlと呼ばれるパッケージに集約されています。これはバージョン1.6以前から行われてきた取り組みです。

バージョン2.0からはStructured Streamingと呼ばれる、DataFrame/Datasetベースのストリーム処理エンジンが試験的に導入されました。現段階ではアルファリリースという位置付けですが、Spark Streamingの延長ではない、Sparkにおける新しいストリーム処理エンジンです。

Spark Streamingでは、従来はRDDをストリーム処理向けにラップしたDStreamと呼ばれるデータ構造を用いていました。しかしながら、RDDベースのバッチ処理との相互運用や切り替えは必ずしも楽ではない点が指摘されていました。Structured Streamingの取り組みにより、ストリーム処理でDataFrameを用いることができるようになりました。これにより以下の3点の効果が期待できます。

  • バッチ処理との相互運用や切り替えの容易化
  • DataFrameベースの処理となることでオプティマイザによる最適化の恩恵を受けられる
  • イベントタイムウィンドウ集約処理をサポート、イベントの生起時間を考慮したウィンドウ集約が可能に

とくに3点目は、データの遅延などにより、必ずしもデータの到着順序とデータに意味付けされたイベントの発生順序が一致しないことがあり、ストリーム処理においてよく取り上げられる課題です。この点を解消する取り組みが進められており、バージョン 2.1から導入され始めました

NTTデータでもSparkで時系列データを扱う事例がありますが、この機能向上は利用者にとってはありがたいものです。

Structured Streamingはバージョン 2.0で導入されていましたが、まだアルファリリース扱いでした。2017年前半の中心的なバージョンになると考えられるバージョン2.1でも、まだアルファリリースのままだと記載されています

バージョン2.1ではStructured StreamingがKafka 0.10以降のバージョンと連携させた動作をできるようになりましたが、この他にもStructured Streamingの開発は順次進んで行くものと思われます。

その他の2017年の予測ですが、ハードウェアの進化にさらに追従する取り組みとして、従来より検討が進められてきたGPU対応もいよいよ本格化するかもしれません。また、DataFrameの利用を基点としてさらに機能の広がりをみせるかもしれません。

たとえば、Sparkの開発で中心となっている米DatabricksがSparkとは別のリポジトリで、米Googleがオープンソース化した機械学習ライブラリをDataFrame上で動作させるようにしたTensorFramesや、 DataFrame上に構築されたグラフ処理ライブラリGraphFramesを開発しています。これらのような、Spark本体に導入されないまでも、Sparkの機能を利用する形で利用用途を独自に拡張するものがこれから増えてくる可能性があります。

Sparkは毎年目覚ましい進歩を遂げるため、軽くウォッチしているだけではキャッチアップがやや大変かもしれませんが、年を追うごとに利用者目線で便利になっていることはまず間違いありません。実際に筆者も「このアプリの書き方だけで分散処理してくれるのすごいよね」という利用者の声を耳にしたことがあります。NTTデータも2015年より「Spark構築・運用ソリューション」を開始し、皆様のSpark導入・運用のご支援をさせていただいております。これまでSparkの様子を伺うだけだった方も、必要に応じてこのようなサービスを利用することも念頭に置きつつ、2017年という年初をきっかけとしてSparkを始めてみてはいかがでしょうか。

おすすめ記事

記事・ニュース一覧