この記事ではTreasure Dataでの効率的なHiveクエリの書き方を紹介したいと思います。
Time Index pushdown を利用する
Treasure Dataを既にご利用な方なら知っているとは思いますが、Treasure Dataを効率的に利用するには取得したいデータの時間を絞ることによって効率的にデータを抽出することができます。
この機能を使わないと全データがクエリの対象となり、Jobの実行時間が長くなり非効率なものとなってしまいます。
実はこの機能、下記のような通常のクエリ
select id from foo where time >= 1349393020
は、正常に動作していたのですが、
select a.id from (select id from foo where time >= 1349393020) a join (select id from foo where time >= 1349393020 - 3600) b on a.id = b.id
のようなself join等サブクエリを使用するとHiveの最適化のせいでTime Index pushdownが利かないというような現象が起きていました。これを回避するためにサブクエリを別Jobとして実行するように
select a.id from (select id from foo where time >= 1349393020 order by id) a join (select id from foo where time >= 1349393020 - 3600 order by id) b on a.id = b.id
わざと無意味なorder byを入れてHiveが最適化しないようなナレッジもありました。
現在はこの機能は改良され正常に動作するようになっています。
実装
さて、どのようにこの機能を実装しているのかというとHiveのFilter Pushdownという機能を利用しています。
TableScanDesc.FILTER_EXPR_CONF_STRを取得することによってfilter expressionを取得することができます。これを利用することによってHiveのアレやコレやをできるようになるでしょう。ただ、こいつの難点はHiveクエリが最適化された場合に値がセットされないということです。これを回避するには最適化が入る前にストレージ層でゴニョゴニョとする必要があります。実際にはHiveStoragePredicateHandlerを継承したクラスを作り、そいつにゴニョゴニョとやらせればTableScanDesc.FILTER_EXPR_CONF_STRを使った時どうようにあれやこれやできることになります。
Hiveのクエリがエラーを吐く
これはTreasure Dataに限ったことではないのですが、Hiveのクエリを書いて実行するとSQLは正常なのにエラーとなる場合があります。Hiveは通常のSQLとは違うため普通に書いても動作しないのは困りますね。
サブクエリ化する
以前までのHiveクエリでunionを利用するにはサブクエリ化する必要がありました。Hive 0.13以降はこの制限が撤廃されトップでも使用できるようになっています。たぶん、この名残だとは思うのですが、普通に書いても通らなかったSQLが特定部分をサブクエリ化することによってJobが実行できるようになることが多々あります。たぶん、Hiveのプランナーが。。。
ということで、普通に書いても通らなかったSQLはサブクエリにできるようなとこはサブクエリ化してしまい、Jobを実行してみましょう。
まとめ
Treasure Dataでのクエリを効率的に利用する方法とエラーになる場合のワークアラウンドを紹介してきました。なんかおかしいなと思ったらこの辺りを思い出してもらえれば幸いです。