Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Impalaにファイルで投入したデータはいつから検索可能になるの?

こんにちは。kimukimu(@)です。

このエントリはSpark, SQL on Hadoop etc. Advent Calendar 2014 - Qiitaの12/14分です。

多くの人がSQLなら書けるということで、
開発者でなくても自在に検索を定義可能なSQL on Hadoopが今非常に広まりつつあります。
私はそんなSQL on Hadoopの中でImpalaを今使用しているため、Impalaについて書かせていただきますね。
f:id:acro-engineer:20141209070857j:plain

Impalaをデータの集計で使用しているのですが、
その際に「元々存在しているテーブルにデータをファイルで投入する」ことがあります。


ですが、ファイル投入後クエリを実行してみても投入した結果が表示されないことがありました。
かつ、Impalaのサイトを見てもどのタイミングでデータが検索可能になるか、は明確には見つからないんですよね・・・

そのため、「Impalaにファイルで投入したデータはいつから検索可能になるの?」が
気になったので確認してみました。

1.前提環境

今回の確認は下記の環境で確認しています。

  • CDH 5.2.0(Cloudera Managerで構築、完全分散モード)
  • CentOS 6.6

Cloudera Managerを使うとHadoopクラスタ構築が本当に楽になりますね。

2.使用するファイル

実際に投入して確認してみるファイルの内容は下記です。

いつから検索可能になるかを確認できればいい、ということと、
あとは確認をわかりやすくするため、各ファイルには必要最小限のデータのみ定義しています。
■201412141000.csv

2014-12-14 10:00:00,Record1,100,12.34,Record1 Comment

■201412141100.csv

2014-12-14 11:00:00,Record2,200,34.56,Record2 Comment

■201412141200.csv

2014-12-14 12:00:00,Record3,300,45.67,Record3 Comment

■201412141300.csv

2014-12-14 13:00:00,Record4,400,56.78,Record4 Comment

2.ファイル投入後の表示タイミング確認パターン

Impalaに対してファイルで投入したテーブルの内容を表示するための関連オペレーションとして、
下記の4つの処理があります。

  1. Impalaにテーブル(EXTERNAL TABLE)を定義する。
  2. 定義したテーブルに対してクエリを発行し、結果を表示する。
  3. ファイルを投入する。
  4. ファイルを削除する。

すると、ざっと考えて下記くらいのパターンが挙げられると思います。
ファイル追加/ファイル削除の順番入れ替えはパターンも多くなるので省きます。

  • A.テーブル定義→クエリ発行(1→2、基本)
  • B.テーブル定義→ファイル追加→クエリ発行(1→3→2)
  • C.テーブル定義→クエリ発行→ファイル追加→クエリ発行(1→2→3→2)
  • D.テーブル定義→ファイル削除→クエリ発行(1→4→2)
  • E.テーブル定義→クエリ発行→ファイル削除→クエリ発行(1→2→4→2)
  • F.テーブル定義→ファイル追加&削除→クエリ発行(1→3&4→2)
  • G.テーブル定義→クエリ発行→ファイル追加&削除→クエリ発行(1→2→3&4→2)

3.確認結果

では各パターン毎に実際に実行してみて結果がどうなるかを確認してみます。
まず下準備としてimpalaユーザの操作可能なディレクトリ上に先ほど定義したファイルを配置します。
その上でHDFS上にImpalaがアクセス可能な下記のディレクトリを作成しておきます。

/ImpalaTest/TestPattern1
/ImpalaTest/TestPattern2
/ImpalaTest/TestPattern3
/ImpalaTest/TestPattern4
/ImpalaTest/TestPattern5
/ImpalaTest/TestPattern6
/ImpalaTest/TestPattern7

結果上では通常のLinux impalaユーザのコマンドを「>」、impala-shellからの実行コマンドを「[impala:21000] >」と記述しています。
また、impala-shellからimpala-daemonへの接続といった共通コマンドや、冗長な記述は省いています。

3-A.テーブル定義→クエリ発行(1→2、基本)

> hadoop fs -copyFromLocal 20141214* /ImpalaTest/TestPattern1
[impala:21000] > CREATE EXTERNAL TABLE test_pattern_1(record_time TIMESTAMP, record_name STRING, record_value_int INT, record_value_float FLOAT, record_comment STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LOCATION '/ImpalaTest/TestPattern1';
[impala:21000] > select * from test_pattern_1 ORDER BY record_time;
+---------------------+-------------+------------------+--------------------+-----------------+
| record_time         | record_name | record_value_int | record_value_float | record_comment  |
+---------------------+-------------+------------------+--------------------+-----------------+
| 2014-12-14 10:00:00 | Record1     | 100              | 12.34000015258789  | Record1 Comment |
| 2014-12-14 11:00:00 | Record2     | 200              | 34.56000137329102  | Record2 Comment |
| 2014-12-14 12:00:00 | Record3     | 300              | 45.66999816894531  | Record3 Comment |
| 2014-12-14 13:00:00 | Record4     | 400              | 56.77999877929688  | Record4 Comment |
+---------------------+-------------+------------------+--------------------+-----------------+

データを投入してからテーブルを定義し、クエリを発行するという基本パターンですので、当然のことながら全データが表示されます。

3-B.テーブル定義→ファイル追加→クエリ発行(1→3→2)

> hadoop fs -copyFromLocal 2014121410* /ImpalaTest/TestPattern2
> hadoop fs -copyFromLocal 2014121411* /ImpalaTest/TestPattern2
[impala:21000] > CREATE EXTERNAL TABLE test_pattern_2(record_time TIMESTAMP, record_name STRING, record_value_int INT, record_value_float FLOAT, record_comment STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LOCATION '/ImpalaTest/TestPattern2';
> hadoop fs -copyFromLocal 2014121412* /ImpalaTest/TestPattern2
> hadoop fs -copyFromLocal 2014121413* /ImpalaTest/TestPattern2
[impala:21000] > select * from test_pattern_2 ORDER BY record_time;
+---------------------+-------------+------------------+--------------------+-----------------+
| record_time         | record_name | record_value_int | record_value_float | record_comment  |
+---------------------+-------------+------------------+--------------------+-----------------+
| 2014-12-14 10:00:00 | Record1     | 100              | 12.34000015258789  | Record1 Comment |
| 2014-12-14 11:00:00 | Record2     | 200              | 34.56000137329102  | Record2 Comment |
| 2014-12-14 12:00:00 | Record3     | 300              | 45.66999816894531  | Record3 Comment |
| 2014-12-14 13:00:00 | Record4     | 400              | 56.77999877929688  | Record4 Comment |
+---------------------+-------------+------------------+--------------------+-----------------+

テーブル定義後にファイルを追加した場合でも、クエリを発行する前に追加しておけば追加結果が反映されることがわかります。

3-C.テーブル定義→クエリ発行→ファイル追加→クエリ発行(1→2→3→2)

> hadoop fs -copyFromLocal 2014121410* /ImpalaTest/TestPattern3
> hadoop fs -copyFromLocal 2014121411* /ImpalaTest/TestPattern3
[impala:21000] > CREATE EXTERNAL TABLE test_pattern_3(record_time TIMESTAMP, record_name STRING, record_value_int INT, record_value_float FLOAT, record_comment STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LOCATION '/ImpalaTest/TestPattern3';
[impala:21000] > select * from test_pattern_3 ORDER BY record_time;
+---------------------+-------------+------------------+--------------------+-----------------+
| record_time         | record_name | record_value_int | record_value_float | record_comment  |
+---------------------+-------------+------------------+--------------------+-----------------+
| 2014-12-14 10:00:00 | Record1     | 100              | 12.34000015258789  | Record1 Comment |
| 2014-12-14 11:00:00 | Record2     | 200              | 34.56000137329102  | Record2 Comment |
+---------------------+-------------+------------------+--------------------+-----------------+
> hadoop fs -copyFromLocal 2014121412* /ImpalaTest/TestPattern3
> hadoop fs -copyFromLocal 2014121413* /ImpalaTest/TestPattern3
[impala:21000] > select * from test_pattern_3 ORDER BY record_time;
+---------------------+-------------+------------------+--------------------+-----------------+
| record_time         | record_name | record_value_int | record_value_float | record_comment  |
+---------------------+-------------+------------------+--------------------+-----------------+
| 2014-12-14 10:00:00 | Record1     | 100              | 12.34000015258789  | Record1 Comment |
| 2014-12-14 11:00:00 | Record2     | 200              | 34.56000137329102  | Record2 Comment |
+---------------------+-------------+------------------+--------------------+-----------------+

テーブル定義後、クエリを実行した後にファイルを追加した場合は追加結果はそのままでは反映されないようです。
そのため、クエリ初回実行時に実際に検索対象となるデータに対して何かしらのインデックス処理のようなものを施している・・?
尚、これは1日たっても結果はそのままでしたので、タイミング問題、というわけでもないようです。

3-D.テーブル定義→ファイル削除→クエリ発行(1→4→2)

> hadoop fs -copyFromLocal 20141214* /ImpalaTest/TestPattern4
[impala:21000] > CREATE EXTERNAL TABLE test_pattern_4(record_time TIMESTAMP, record_name STRING, record_value_int INT, record_value_float FLOAT, record_comment STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LOCATION '/ImpalaTest/TestPattern4';
> hadoop fs -rm /ImpalaTest/TestPattern4/201412141300.csv
[impala:21000] > select * from test_pattern_4 ORDER BY record_time;
+---------------------+-------------+------------------+--------------------+-----------------+
| record_time         | record_name | record_value_int | record_value_float | record_comment  |
+---------------------+-------------+------------------+--------------------+-----------------+
| 2014-12-14 10:00:00 | Record1     | 100              | 12.34000015258789  | Record1 Comment |
| 2014-12-14 11:00:00 | Record2     | 200              | 34.56000137329102  | Record2 Comment |
| 2014-12-14 12:00:00 | Record3     | 300              | 45.66999816894531  | Record3 Comment |
+---------------------+-------------+------------------+--------------------+-----------------+

削除についても追加と同じく、テーブル定義後クエリ発行前に実施しておけば反映されるようです。

3-E.テーブル定義→クエリ発行→ファイル削除→クエリ発行(1→2→4→2)

> hadoop fs -copyFromLocal 20141214* /ImpalaTest/TestPattern5
[impala:21000] > CREATE EXTERNAL TABLE test_pattern_5(record_time TIMESTAMP, record_name STRING, record_value_int INT, record_value_float FLOAT, record_comment STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LOCATION '/ImpalaTest/TestPattern5';
[impala:21000] > select * from test_pattern_5 ORDER BY record_time;
+---------------------+-------------+------------------+--------------------+-----------------+
| record_time         | record_name | record_value_int | record_value_float | record_comment  |
+---------------------+-------------+------------------+--------------------+-----------------+
| 2014-12-14 10:00:00 | Record1     | 100              | 12.34000015258789  | Record1 Comment |
| 2014-12-14 11:00:00 | Record2     | 200              | 34.56000137329102  | Record2 Comment |
| 2014-12-14 12:00:00 | Record3     | 300              | 45.66999816894531  | Record3 Comment |
| 2014-12-14 13:00:00 | Record4     | 400              | 56.77999877929688  | Record4 Comment |
+---------------------+-------------+------------------+--------------------+-----------------+
> hadoop fs -rm /ImpalaTest/TestPattern5/201412141200.csv
> hadoop fs -rm /ImpalaTest/TestPattern5/201412141300.csv
[impala:21000] > select * from test_pattern_5 ORDER BY record_time;
WARNINGS: Failed to open HDFS file hdfs://cluster1:8020/ImpalaTest/TestPattern5/201412141200.csv
Error(2): No such file or directory
Backend 1:Failed to open HDFS file hdfs://cluster1:8020/ImpalaTest/TestPattern5/201412141200.csv
Error(2): No such file or directory

クエリを実行した後にファイルを削除した場合、その後再度クエリを実行するとファイルが存在しない旨のエラーとなります。
結果も表示されませんでした。
やはりクエリの初回実行時にファイル単位のインデックス作成に近いものを行っていて、
以後クエリを実行した場合はそのインデックスにそって検索を行うようです。

・・と、とりあえずここまでで大体動きの予測はつきますが、念のため追加と削除を両方行うパターンについても確認しておきます。

3-F.テーブル定義→ファイル追加&削除→クエリ発行(1→3&4→2)

> hadoop fs -copyFromLocal 2014121410* /ImpalaTest/TestPattern6
> hadoop fs -copyFromLocal 2014121411* /ImpalaTest/TestPattern6
> hadoop fs -copyFromLocal 2014121412* /ImpalaTest/TestPattern6
[impala:21000] > CREATE EXTERNAL TABLE test_pattern_6(record_time TIMESTAMP, record_name STRING, record_value_int INT, record_value_float FLOAT, record_comment STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LOCATION '/ImpalaTest/TestPattern6';
> hadoop fs -copyFromLocal 2014121413* /ImpalaTest/TestPattern6
> hadoop fs -rm /ImpalaTest/TestPattern6/201412141200.csv
[impala:21000] > select * from test_pattern_6 ORDER BY record_time;
+---------------------+-------------+------------------+--------------------+-----------------+
| record_time         | record_name | record_value_int | record_value_float | record_comment  |
+---------------------+-------------+------------------+--------------------+-----------------+
| 2014-12-14 10:00:00 | Record1     | 100              | 12.34000015258789  | Record1 Comment |
| 2014-12-14 11:00:00 | Record2     | 200              | 34.56000137329102  | Record2 Comment |
| 2014-12-14 13:00:00 | Record4     | 400              | 56.77999877929688  | Record4 Comment |
+---------------------+-------------+------------------+--------------------+-----------------+

予想通り、ではありますが、クエリを初回実行前にファイルの追加削除を両方行っても問題なく検索可能です。

3-G.テーブル定義→クエリ発行→ファイル追加&削除→クエリ発行(1→2→3&4→2)

> hadoop fs -copyFromLocal 2014121410* /ImpalaTest/TestPattern7
> hadoop fs -copyFromLocal 2014121411* /ImpalaTest/TestPattern7
> hadoop fs -copyFromLocal 2014121412* /ImpalaTest/TestPattern7
[impala:21000] > CREATE EXTERNAL TABLE test_pattern_7(record_time TIMESTAMP, record_name STRING, record_value_int INT, record_value_float FLOAT, record_comment STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LOCATION '/ImpalaTest/TestPattern7';
[impala:21000] > select * from test_pattern_7 ORDER BY record_time;
+---------------------+-------------+------------------+--------------------+-----------------+
| record_time         | record_name | record_value_int | record_value_float | record_comment  |
+---------------------+-------------+------------------+--------------------+-----------------+
| 2014-12-14 10:00:00 | Record1     | 100              | 12.34000015258789  | Record1 Comment |
| 2014-12-14 11:00:00 | Record2     | 200              | 34.56000137329102  | Record2 Comment |
| 2014-12-14 12:00:00 | Record3     | 300              | 45.66999816894531  | Record3 Comment |
+---------------------+-------------+------------------+--------------------+-----------------+
> hadoop fs -copyFromLocal 2014121413* /ImpalaTest/TestPattern7
> hadoop fs -rm /ImpalaTest/TestPattern7/201412141200.csv
[impala:21000] > select * from test_pattern_7 ORDER BY record_time;
WARNINGS: Failed to open HDFS file hdfs://cluster1:8020/ImpalaTest/TestPattern7/201412141200.csv
Error(2): No such file or directory
Backend 1:Failed to open HDFS file hdfs://cluster1:8020/ImpalaTest/TestPattern7/201412141200.csv
Error(2): No such file or directory

予想通り、クエリを1回実行後にファイルの追加削除を行った場合、対応できませんでした。

4.テーブル定義後にファイルの追加削除を行った場合結果に反映させるには?

では、ファイルの追加削除を反映させるためにはどうすればいいのでしょうか。
Impalaクエリのマニュアルを見てみると「REFRESH」というコマンドがありました。
REFRESHコマンドを実行することで、追加削除の結果が反映され、現状のファイルに対して検索を行うことが可能でした。

[impala:21000] > REFRESH test_pattern_7;
[impala:21000] > select * from test_pattern_7 ORDER BY record_time;
+---------------------+-------------+------------------+--------------------+-----------------+
| record_time         | record_name | record_value_int | record_value_float | record_comment  |
+---------------------+-------------+------------------+--------------------+-----------------+
| 2014-12-14 10:00:00 | Record1     | 100              | 12.34000015258789  | Record1 Comment |
| 2014-12-14 11:00:00 | Record2     | 200              | 34.56000137329102  | Record2 Comment |
| 2014-12-14 13:00:00 | Record4     | 400              | 56.77999877929688  | Record4 Comment |
+---------------------+-------------+------------------+--------------------+-----------------+

5.確認結果まとめ

これまでの結果をまとめると下記のようになります。

  1. ImpalaはEXTERNALテーブル定義後、クエリを初回実行した際にインデックスのようなものを作成する。
  2. インデックス(?)を作成する前に行ったファイル追加削除はクエリ実行結果に反映される。
  3. インデックス(?)を作成した後に行ったファイル追加削除はクエリ実行時に反映されない。(削除の場合はエラーになる)
  4. REFRESHコマンドを実行することで現状の最新の状況にインデックス(?)が更新される。

テーブル定義後、データを投入しながらインクリメンタルにクエリを発行する場合、
データ投入時の事後処理かクエリ発行時の事前処理でREFRESHコマンドを実行すればOK
となりますね。
これで追加しながら検索、もデータ未反映を気にすることなく実行可能ですね。

小ネタでしたが、以上です。

Acroquest Technologyでは、キャリア採用を行っています。


  • 日頃勉強している成果を、Hadoop、Storm、NoSQL、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
  • 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
  • OSSの開発に携わりたい。

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
 キャリア採用ページ