Hadoopはどのように動くのか ─並列・分散システム技術から読み解くHadoop処理系の設計と実装

第19回Impalaの設計と実装[3]

はじめに

今回は、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上のファイルパスを受け渡すことにより実現されていましたが、セキュリティの観点から、現在はファイルディスクリプタを受け渡すことにより実現されています

IO ManagerによるI/Oの並列化と先読み

次に、Impaladの内部でI/O処理を管理するIO Managerについて説明します。IO Managerの特徴は大きく下記の2つです。

  1. I/O処理の先読み
  2. ディスクごとの専用のスレッドを用いた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の関係
図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処理
図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量をさらに削減できる場合があります。

おわりに

3回に渡ってImpalaの内部を説明してきましたが、いかがでしたでしょうか。Impalaがアドホッククエリを処理するために行っている並列化や高速化の技法の概観を理解していただけたなら幸いです。

次回からは、Apache Sparkについて、コミッタの猿田さんから紹介いただく予定です。

謝辞

今回Impalaの記事執筆にあたってレビューやアドバイスを受けた嶋内翔はじめClouderaのメンバーに感謝します。

おすすめ記事

記事・ニュース一覧