読者です 読者をやめる 読者になる 読者になる

よしだのブログ

サブタイトルはありません。

詳説 4 Tips of using Apache Spark (LT してきました!@ 第2回 Learning Spark 読書会)

こんばんは!

去る日曜日に行われました、Learning Spark 読書会にてお時間を頂いて LT をさせていただきました。手持ちの小ネタを集めたコネタ集的な LT でしたが、参考になったなどの感想いただき、とても嬉しかったです!また、ネタがまとまったら機会をいただければと思います。

前フリはここまでにして。以下が、その時のスライドです。

今日のエントリは、その時お話した LT の解説をしたいと思います。コピペでもなんでもして使っていただければ。内容の真偽についてはできるだけ、正しいことを確認していますが、ご自分の責任でくれぐれもご利用くださいませ。バージョンは spark 1.2.1 を前提とします。

その1:Easiest way running Spark on Cluster

自分でアプリを作れるようになったら、Spark の速さを体感したくなると思います。そこで、ひとつのお手軽な方法として、Amazon EMR で Spark を動かすという方法があります。実際のやり方については、こちらのエントリを参照ください。

blog.yoslab.com

ちなみに、さきほど確認したら、もう 1.3.0 にも対応していました!素晴らしいですね。

上記のエントリでは、記載していませんが bootstrap-action を使うと、さらにクラスタのセットアップが楽になります。よくあるのが、自分のアプリを作ってその中から他のライブラリの jar を使うようにすると、大量の jar に依存していてクラスタ上にデプロイするのがシンドイという場合があります。bootstrap-action では、このようなケースを楽にできます。

bootstrap-action は、実体としてはただの bash スクリプトです。このスクリプトを指定して、create-cluster すると、クラスタの起動処理の中で、クラスタ内の各サーバー上で root 権限でこのスクリプトを実行してくれます。*1 なので、bootstrap-action の中で、scala を入れて、sbt を入れて、git-clone して、sbt package すると、sbt が自動的に必要な jar を落としてきてくれるので、いちいち大量の jar をクラスタ上に配置する手間がなくなります。唯一の欠点としては、github や bitbucket などのオンラインの git レポジトリにコードを置いておく必要があります。

参考に、実際に作成したスクリプトを貼っておきます。

gist084f66eba95adfe5fdf6

その2:オプションの指定順序を間違えると、パラメータが無視される。

自作したアプリケーションを起動する際は bin/spark-submit コマンドを呼び出すと思います。この時に指定するオプションには、自作の jar とクラス以外にも、色々なオプションがあります。例えば、クラスター環境で起動するときには、使用するメモリ、CPU、executor の数などなど指定することができます。 このオプションですが、指定する順番を間違えると、指定した値が無視され、デフォルト値で起動します。

いつものコマンドラインツールのつもりで使うと痛い目にあうという例です。。

まず、ダメな例のコマンドラインです。これでアプリを起動します。 urge-recommend-user-to-item_2.10-0.1.jar というのが、アプリの jar でそこから後ろの rates.txt までがアプリの引数、そこから後ろはそれ以外のオプションになります。適当な場所で開業しえいますが、実際には一行です。

~/spark/bin/spark-submit
--master yarn-cluster 
--jars  (省略) 
--class RecommendU2I 
urge-recommend-user-to-item_2.10-0.1.jar
s3n://abc-takumiyoshida/datasets/ s3n://abc-takumiyoshida/rates.txt 
--driver-memory 2g --num-executors 4 --executor-memory 4g --executor-cores 2

以下は、実行後に Spark の Web UI で、コマンドラインを見ることができる画面です。色付きでハイライトされている箇所にご注目いただきたいのですが、 --num-executors が2つあるのがわかると思おいます。実際の動作を観察していると、後ろで指定されている --num-executors 2 が採用されて動いていました。すなわち、指定した 4 は無視されています。。

f:id:yoshi0309:20150401230740p:plain

そこで、以下のとおり、ドキュメントに記載されている順番で並べ替えて起動すると、きちんと呼んでくれるようになりました。

~/spark/bin/spark-submit 
--class RecommendU2I 
--master yarn-cluster 
--driver-memory 2g --num-executors 5 --executor-memory 2g --executor-cores 4 
--jars (省略) 
urge-recommend-user-to-item_2.10-0.1.jar 
s3n://abc-takumiyoshida/datasets/ s3n://abc-takumiyoshida/rates.txt

ドキュメントから、全体を引用すると以下のようになります。

./bin/spark-submit \
--class <main-class>
--master <master-url> \
--deploy-mode <deploy-mode> \
--conf <key>=<value> \
... # other options
<application-jar> \
[application-arguments]

メインクラスを指定する --class が先頭で、アプリの jar が離れているのが罠です。。

その3:Do Not Nest RDD

RDD をネストしてループすると NullPointerException が出る件ですが、こちらは一度エントリを書いていますので、こちらをご参照ください。

blog.yoslab.com

その4:lookup メソッドは激遅なので使わない!

Key-Value 型のタプルを RDD にすると、暗黙変換*2されて使えるようになる PairRDDFunction があるのですが、これは非常に便利なメソッドがそろっています。Key-Value 型のデータに限定されますので、RDD に無いとても便利なメソッドが多いです。以下、一例を記載すると

  • join / leftOuterJoin/ rightOuterJoin- 2つの RDD を join する。
  • countByKey- Key の値ごとに件数を数える。
  • reductByKey - Key の値ごとに reduce する。
  • groupByKey - Keyの値ごとでグルーピングする (value が key ごとに Seq にまとまる)
  • lookup - Key の値で value を検索する

特に join は強力で、RDD のネストが出来なくても join ができればなんとかなったりします。

とまあ、便利なメソッドが揃っているのですが、lookup は非常に遅い!ので、できるだけ使わないほうがよいです。特に繰り返しては。。この lookup は指定した key の値を持つ value を RDD から引っ張ってくる、というメソッドです。

以下は、Spark Web UI から、lookup の箇所をピックアップしたスクリーンショットです。Input のデータ量がわずか、240MB に対し、なんと lookup 一発で 11秒かかっていることも驚きです。ちゃんと cache もしていてこれですよ。。。

f:id:yoshi0309:20150401233733j:plain

user mailing list のアーカイブによれば、これは私のミスでもなんでもなく、そういう仕様だそうです。以下が、該当するスレッドです。要するに、全件舐めないといけないから遅いのよと、そういう話ですが、それにしてもって感じではあります。

http://mail-archives.us.apache.org/mod_mbox/spark-user/201502.mbox/%3CCAMAsSdLYXmJuwc084MzKJWq4hpSk66jCLp3MwhZULT6+gqjagQ@mail.gmail.com%3E

lookup を避けるには、map や filer で抽出する、抽出したい key が複数あるばあいは join するなどの方法が考えられます。どれを使うかは、作成しているアプリの仕様次第なので、適当なものを選んでもえればというところです。

ちなみに join ですが、いわゆる inner join なので、両方の RDD に同じ key があるデータしか帰ってきませんので、抽出にも使いやすいと思います。詳細は、以下よりどうぞ。

http://spark.apache.org/docs/1.2.1/api/scala/index.html#org.apache.spark.rdd.PairRDDFunctions



といわけで以上、小ネタだらけでしたがご参考まで!

教科書はこちら。

普通の技術書と違い、本当に教科書っぽい書き方なので、1から勉強するにも向いていると思います。おすすめです。

Learning Spark: Lightning-Fast Big Data Analysis

Learning Spark: Lightning-Fast Big Data Analysis

*1:ちなみに、実行するパスは / でした。

*2:Scalaの場合、Key-Value のデータのRDDの場合、暗黙変換で使えるようになる。