Edited at

Impala 落ち穂拾い

More than 1 year has passed since last update.

この投稿はDistributed computing (Apache Hadoop, Spark, ...) Advent Calendar 2016の22日目です。

昨日はkiszkさんのSparkの記事でした。


この記事の概要

Impalaを使い込んでいる人向けの、役に立つ小ネタ集という位置づけ。

Impala入門ではないので、Impala 入門のような情報は下記のようなページを参照すること。


前提

最低限以下の資料を読んで理解しておくこと。

以下の資料も読んでおけば完璧。

Impala関連資料は以下にまとまっているのでこちらにも目を通すといい。


高頻度のDDLはやってはいけない

現行の Impala はどんなに頑張っても 1 DDL / sec 程度のスループットしか出すことができない。

高頻度のDDLを実行するような運用だとスループットを出すことが不可能になるので注意。

典型的なアンチパターンとしては、ダイナミックパーティショニングの濫用などがある。


ファイルフォーマット + HBase の使い分け

Parquet は分析クエリの実行は速いが書き込みは非常に遅い。

Avro は書き込みは速いがINSERTはできない(Hiveからは可能)。

HBase は書き込みも速くINSERTも可能だがフルスキャンは非常に遅い。

テキストは分析クエリの実行はそれほど速くないし圧縮効率もよくないが、取り回しが一番簡単。

このように、ファイルフォーマットはどれも一長一短であり、適切なフォーマットの選択が性能や利便性を決める。

Impalaのドキュメントにある、以下の指針を参考にするといい。


  • 原則として、速度やリソース使用効率に問題がない場合は元のフォーマットを使い続けた方がいい。その方が取り回しが楽になる。

  • Parquet は、上記のような課題があるデータに対してのみ適用する方がいい。例えば、ファクトテーブルなどは明らかにこれに該当するデータセットとなるだろう。

  • HBase はフルスキャンが遅いものの、そうでないクエリに対しては十分高速に働く。例えば、ディメンションテーブルとして活用する分には十分使いやすい。

参考: http://www.cloudera.com/documentation/enterprise/latest/topics/impala_file_formats.html


SSDは費用対効果が低い

Impalaはスキャン時に型変換処理やUDFなどを実行するので大抵の場合は性能ボトルネックがCPU依存となる。

よって、ディスクを SSD に変えてもほとんどの場合、費用対効果は高くない。

CPUがボトルネックかどうかを確認するのであれば、UDFなどの処理を抜いてみて性能を計測すればいい。


Impala の S3 対応

Impala 2.6 (CDH 5.8) から S3 に対してSQLを発行できるようになったが、S3 に対する性能は HDFS よりも確実に遅いので、分析クエリの性能を必要とする場合は今まで通り HDFS にロードすること。

とはいえ、HDFS にロードする場合、S3 からの転送 + COMPUTE STATS の時間が発生するため、1-2回クエリを実行する程度であればトータルの時間はS3に直接投げた方が速い可能性が高い。

流すワークロードによってストレージを選択すること。

参考: http://www.cloudera.com/documentation/enterprise/latest/topics/impala_s3.html


アドミッションコントロールは並列性維持のためには必須

Impala の高い並列性を確保するためにはアドミッションコントロールは必須となる。並列実行時に問題があって、かつアドミッションコントロールを利用していない場合は今すぐ設定すること。

アドミッションコントロールの説明は下記ページに書いてある……が、長い。

http://www.cloudera.com/documentation/enterprise/latest/topics/impala_admission.html

Howto及び設定例は以下のページに書いてあるのでこちらをまず読むといいだろう。

http://www.cloudera.com/documentation/enterprise/latest/topics/impala_howto_rm.html

簡単に指針について説明すると、以下のような感じになる。


  • アドホッククエリ用のリソースプール: メモリ割り当て少なめ、同時実行クエリ数制限なし、キュータイムアウト時間短め

  • レポート用のリソースプール: メモリ割り当て多め、同時実行クエリ数少なめ、キュータイムアウト時間長め

アドホッククエリは多くの場合それほどメモリを消費しないし、多少メモリを使うクエリが実行されたとしても順次実行していけば現実的な時間で応答を返すことができる。

また、書き間違い等によって無駄にメモリを消費するクエリが流れたとしても(残念ながら現場では頻繁に発生する)、クラスタ全体のメモリの一部しか食い潰さないため、レポート用クエリの実行に影響を与えることはない。アドホッククエリのリソースプールには多くの人が小さなクエリを発行するため、同時実行クエリ数には制限をかけない。一方でキューのタイムアウト時間を短めに設定しておけば、クラスタが「混んでいる」場合にはすぐに実行が失敗し、ユーザが気づくことができる。

一方、レポート用リソースプールは多くのメモリを消費しても問題ないように割り当てる。同時実行数に制限をかけ、キューのタイムアウト時間を長めに設定しておけば、レポート用クエリをキューに加えて「順番待ち」させることができる。


アドミッションコントロールとロードバランシング

Impalaのリソース管理機構であるアドミッションコントロールは、SPOFがない代わりに statestored を通して impalad 同士でリソース情報を通信していくため、ソフトリミットとなっている。

通常 impala にクエリを投げる場合は前段にロードバランサを設置することで全ての impalad に別々にクエリを投げるようにするが、LBの利用とアドミッションコントロールは相性が悪い。

もしどうしても厳密なリソース管理を行う必要がある場合は、LBを使わずに単一のimpaladでのみクエリを受け付けるようにする。

単一だけではクエリをさばき切れないというのであれば、2個、3個と増やしていくことで、負荷分散とリソース管理のバランスをとることができる。


Impala と Hive のクエリの違い

ここでは主なものを示す。


  • カスタム SerDe がない。よって、あらゆるフォーマットをスキーマの形で読む、ということはできない。一旦Hive で読み込んだ後、Impala が読めるフォーマットに変換すること。

  • XML / JSON 関数がない。これも上記と同様、一度 Hive から読んで、CREATE TABLE AS SELECT などで変換してから読み込むこと。

  • サンプリングがない。サンプリングが必要な場合はHiveを使うこと。

  • UDTFがない。UDF / UDAF は実装済み。

  • DATE 型がない

非互換の機能の多くがETL処理によってImpalaに変換可能なものなので、準備に1ステップ増やすだけなのでそれほど苦労はないだろう。

参考: http://www.cloudera.com/documentation/enterprise/latest/topics/impala_langref_unsupported.html


Impala のデータ読み込みスケジューリングと複製数

Impala はデータのローカリティだけでなく、impaladの負荷も見ながらスキャンのスケジューリングを行っていく。例えばノードN1、N2があり、ブロックB1のレプリカがB1_0の1つのみで、N1上にしか存在しない場合を考える。

大抵の場合はN1にスキャンのフラグメントを割り当ててB1_0を読ませにいくが、N1の負荷が高い場合にはN2にスキャン指示を出す場合がある。当然リモートリードになるので性能は落ちる。

この挙動から、HDFSのレプリケーションファクターをデフォルトの3のままにしておくは耐障害性の観点からだけでなくクエリ実行のスループットからも必要であることがわかる。

例えばN1、N2がブロックB1のレプリカB1_0、B1_1をそれぞれ持っていたとき、もしN1の負荷がN2より高ければ、ImpalaはN2にB1_1をスキャンするように指示を出すが、N2はB1_1をローカルリードできるため性能に影響を与えることはない。


Impala のキャッシュ戦略

CDH 5.1 から Impala で HDFS キャッシュが使えるようになった。Impala はデフォルトで HDFS キャッシュを優先して読むようにする。ところが、CDH 5.1 時点では HDFS キャッシュを複製する手段を持たなかった。先述のImpalaのデータ読み込みスケジューリングの挙動を思い出してほしいが、レプリカが1つしかないということは、結局特定のノードに負荷が集中することになり、性能はむしろ劣化する可能性が高くなってしまう。

CDH 5.4 では WITH REPLICATION 句を使うことでキャッシュを複製できるようになった。公式ドキュメントでは複製数を3にすることを推奨している。

しかし、「負荷が低いノードに優先して割り当てる」という挙動だとたまにホットスポットが発生してしまうため、CDH 5.7 からはキャッシュブロックへのスケジューリングはランダムで行うようになった。

さらに、これでもホットスポットが発生する場合を想定して、SCHEDULE_RANDOM_REPLICA クエリオプションを使うことによってキャッシュに乗っていないブロックのスケジューリングもランダムで行うことができるようになった。

そしてCDH 5.9 では、REPLICA_PREFERENCE クエリオプションが導入され、キャッシュ・ローカル・リモートを全て対等のものとして扱うことが可能になった。

こうした設定は全て「同時並列性の向上」を目的としているものであり、並列性に特に問題を感じていない場合は使う必要のないオプションである。

参考


謝辞

Cloudera の Impala/Kudu の守護神 @azurecube にレビューしていただきました。ありがとうございます。

azurecube のアドベントカレンダーネタの Kuduの記事 も合わせてご覧ください。