はじめに
今回は、ImpalaにおけるI/Oの高速化技法について説明します。
前回 説明したように、Impalaの実行エンジンは可能な限りメモリ上で処理をすることでアドホッククエリのレイテンシを下げ、スループットを向上させる、という設計方針で開発されています。
しかし、データはストレージ(二次記憶装置)に格納されているため、当然、ストレージへのI/Oを回避することはできません。また、Impalaは実行時に十分なメモリを確保するべく、データをメモリ上に保持(キャッシュ)しないため、クエリを実行するたびにデータをストレージから読み出すことを前提として設計されています[1] 。
今回は、このようなユースケースを考慮しつつ、高速・高効率なアドホッククエリを実行するためのI/O処理方式とデータレイアウトについて解説します。
Short-Circuit Local ReadsによるI/Oの高効率化
Short-Circuit Local Readsは、HDFS経由のI/Oを高効率化する機能です。これはImpala独自のものではなく、HDFSの機能であり、Apache HBase(以後HBase)などでも使用されています。
通常、HDFSに対してI/Oを発行する場合においては、HDFSのクライアント(たとえばimpalad)がDataNodeを経由してI/O処理を行いますが、Short-Circuit Local Readsを用いる場合においては、DataNodeをバイパスし、HDFSクライアントのプロセスが直接HDFSブロックを読み出します[2] 。この機能により、Impalaにおいて繰り返し実行されるI/O処理のフットプリントを削減することができるため、パフォーマンスの向上が見込めます。
なお、当該機能は、以前はOS上のファイルパスを受け渡すことにより実現されていましたが、セキュリティの観点から、現在はファイルディスクリプタを受け渡すことにより実現されています 。
[2] IImpalaはHDFSブロックの位置情報をキャッシュしているため、Impala外からテーブルのデータファイルを変更された場合など情報が古い場合はShort-Circuit Local Readsを実行できず、HDFSのRemote Readsを実行することになります。ブロックの情報が古い場合は、Invalidate metadataコマンドを実行することで更新できます。
IO ManagerによるI/Oの並列化と先読み
次に、Impaladの内部でI/O処理を管理するIO Managerについて説明します。IO Managerの特徴は大きく下記の2つです。
I/O処理の先読み
ディスクごとの専用のスレッドを用いたI/O処理
I/O処理の先読み
クエリ処理においては、通常、当該処理に必要なレコードが必要になったときに初めてそのレコードの取得要求をディスクドライブなどに発行し、レコードの取得完了を待って、当該レコードを用いてCPU処理を行うという同期的な動作を繰り返して処理を進めていきます。しかしながら、このような方法では、CPUはI/Oの取得完了を待つ必要があり、また、二次記憶装置においてもCPUの入出力命令を待つ必要があるため、必ずしも効率的にCPUや二次記憶装置の性能を引き出すことができない場合があります。
ImpalaのIO Managerにおいては、I/Oの先読みを行うことにより、当該問題の解決を図ります。先読みにおいては、クエリ処理に必要なデータの入出力発行命令を当該データが実際に必要となる前に発行しておきます。事前に読み出したデータはメモリ上のバッファに格納され、CPUは当該バッファ上にあるデータを用いて処理を行います。
また、IO Managerはバッファにデータを格納した後、該当データを使用するCPUの処理を待たずに並行して実行されているほかのクエリなどの次のI/Oの処理を開始することが可能です。
図1 IO ManagerとCPUの関係
ディスクごとの専用のスレッドを用いたI/O処理
Impalaにおいては、ディスクドライブごとに専用のI/Oスレッドを用いて、I/O処理を行います。impalad(IO Manager)が受けつけるクエリおよびクエリのI/O要求は複数が平行に実施されることも想定されており、さまざまなI/O要求に対して継続してレイテンシを抑えた対応を行うため、IO Managerは各ドライブごとのI/Oを精緻にスケジューリングしています。
図2 IO ManagerによるI/O処理
Apache Parquetを用いた列指向データ処理
分析処理においては、本連載第14回 でもかんたんに説明したように、列指向データレイアウトを用いることが一般的になりつつあります。Impalaにおいても、Apache Parquet(以後Parquet ※3 )という列指向データレイアウト機構の使用を推奨しています。
列ごとにデータを管理することにより、属性によっては行ごとにデータをまとめた場合より同一の値が連続する可能性が高くなり、そのような列においては高い圧縮率が期待でき、圧縮データにおけるI/O量を削減できる傾向があります。
ただし、読み出しクエリの実行時においては、圧縮されたデータの展開や、レコード(タプル)の再構築などのCPU処理を余計に行う必要があります。Impalaにおいては、I/O量の削減とCPU処理のオーバーヘッドのバランスをとるべく、snappyと呼ばれる圧縮形式を用いています。
また、Parquetはデータを列指向で保持するだけではなく、各列のヘッダにその列データにおける値の範囲情報を持っています。この情報はIO Managerによって用いられ、クエリに不必要なデータを読み飛ばすことが可能であり、I/O量をさらに削減できる場合があります。
Apache Parquetを使用する際の注意点とApache Kuduプロジェクトについて
Parquetを使用する際の注意点として、データを列指向レイアウトに再構成するために余計に時間を要する点や、データの追記や更新を(直接)行えないという点があります。よって、たとえばETLなどにおける中間データや、更新が頻繁なデータには適さないと考えられます。
しかし、分析用のデータとして更新がされず繰り返し参照されるファイルを作成する場合などには適したフォーマットなので、パフォーマンスの検証などの際にはparquet/snappy化したデータもあわせて確認してみてください。
また、列指向ストレージに高速にデータを追記、更新したいという要望に対応するため、Apache Kudu (Inclubating)プロジェクトが公開されています。本稿執筆時点ではベータ版のプロジェクトですが、高速なデータの更新と列指向ストレージの検索性能を両立させる要件のあるユースケースでは有用な選択肢になっていくことが期待されます。
おわりに
3回に渡ってImpalaの内部を説明してきましたが、いかがでしたでしょうか。Impalaがアドホッククエリを処理するために行っている並列化や高速化の技法の概観を理解していただけたなら幸いです。
次回からは、Apache Sparkについて、コミッタの猿田さんから紹介いただく予定です。
謝辞
今回Impalaの記事執筆にあたってレビューやアドバイスを受けた嶋内翔はじめClouderaのメンバーに感謝します。
Impalaの今後の見通し
今回お伝えしたImpalaのデータアクセスの処理に関連して、Record Service というセキュリティレイヤのプロジェクトも公開されています。これも執筆時点ではベータ版のプロジェクトですが、HiveやSpark SQLなどのSQL on Hadoopから使用されることを想定したプロジェクトで、セキュリティレイヤであるRecord ServiceのデータアクセスにImpalaの技術を応用することで、新しいレイヤを追加しながらSQL処理の高速化も同時に実現しようという野心的なプロジェクトになっています。
KuduやRecord Serviceのような関連プロジェクトも含め、Impalaは非常に活発に開発が進められているプロジェクトですが、じつは2015年にはNested Typesへの対応など機能の拡充に焦点を当てて開発しており、パフォーマンス関連は大きな変更をしてきていませんでした。しかし、2016年はパフォーマンスを大幅に向上させるための施策が検討されており、昨年に発表されたロードマップでは20倍の速度向上を見込んでいることが公表されています 。
キーワードは先のロードマップにも記載されていますが、どのような更新になるのか、今回の内容と見比べながら楽しみにしていてください。
なお、Impala 2.3でのパフォーマンス比較の記事 も公開されています。また、先日公開されたImpala2.5について、パフォーマンスに関する更新やTPC-DSで2.3比+363%の性能向上といった検証結果が報告されています 。現状の確認や追試験、今後のバージョンとの比較などの参考になれば幸いです。