Hiveの機能 | Hadoop Advent Calendar 2016 #08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、小澤です。 この記事はHadoop Advent Calendar 8日目のものとなります。

前回はApache TezというMapReduceに変わるHadoop上での分散処理フレームワークを紹介しました。
今回はHiveを利用していく上で必須であったり、知っておくとパフォーマンスチューニングなどで役に立つ機能を紹介します。
今回の内容ですが、Hiveの実行エンジンにはTez, ファイルフォーマットにORCを使っている前提とします。 すべてではありませんが、いくつかの項目はそれらに依存しているためです。

パーティショニング

これは実用上ではほぼ必須となるものです。 Hiveでのパーティショニングは具体的にはディレクトリを分けることで実現しています。 Hadoopの性質上、同一ファイルに対する追記は考慮していませんし、入出力の基本的な単位もディレクトリになるため、同一ディレクトリにファイルを追加していくということも通常はしません。 そのため、HiveではCREATA TABLE時に指定されたパーティション単位でディレクトリを作りデータの実態はその中に入れていきます。 Hiveでのデータの扱いはこの単位となることが多いので以下の2点は常に意識しておきます。

  • INSERTする際にはパーティション単位で行われる。また、すでに存在するパーティションへのINSERTは置き換えられる
  • SELECTの際はWHERE句でパーティションを指定することによって指定したディレクトリのみを見にいく

通常よくあるパーティションの分け方として日付を利用することが多いので、この扱いは理にかなってると言えるでしょう。

パーティションを指定するのはCREATE TABLE時に

CREATE TABLE t1 (...) PARTITION BY (<パーティション名> <型>) ...;

とします。

データを入れる際は、LOAD DATAであれば

LOAD DATA ... PARTITION (<パーティション名>='<値>');

のように記述します。 また、INSERT SELECTであれば、

INSERT INTO TABLE <テーブル名> PARTITION (<パーティション名>='<値>') SELECT ...;

のように記述することで指定できます。 この時、例えば処理を行った日付をパーティションの値にするいうことであれば、あらかじめSQL文に書いておけばいいので問題ありませんが、SELECTした結果に含まれる日付を利用してパーティションを指定したいなどといった場合は

INSERT INTO TABLE <テーブル名> PARTITION (<パーティション名>) 
SELECT colum1, column2, ..., <パーティション名のカラム> FROM ...;

のようにすることで取得したカラムをパーティションに利用できます。

古いデータは削除したいなどといった場合は

ALTER TABLE <テーブル名> DROP PARTITION (<パーティション名>='<値>');

のようにすることで条件を満たすパーティションを削除してくれます。

CREATE EXTERNAL TABLE

テーブルを作成する際に、CREATE TABLEではなく、CREATE EXTERNAL TABLEと、EXTERNALを追記して作成することができます。 通常だと、Hiveはテーブルやパーティションを削除した際にデータの削除も行いますが、EXTERNALなテーブルに関してはデータの削除は行いません。

安全を期してデータを残しておきたい場合には有効ですが、知らず知らずのうちに溜まった大量のデータがストレージを圧迫する可能性もあるので必要に応じて使い分けてください。

Vectorization

次にVectorizationというものを紹介します。これはHiveの実行を早くするための機能です。 Vectorizationを有効にすることで、1024個の単位でまとめてデータを処理してくれるようになります。

どういうことかというと、通常であればHiveは1行1行に対してなんらかの処理をやっていきます。 これは分散されたそれぞれは並列で処理されているが、一つのコンテナないでは直列で処理していることを意味します。 そのため、処理内容としても1行受け取って結果を返すような関数で実装されていたりします。 これに対してVectorizationを有効にした場合はだと、1024行のデータを一括で受け取り、すべてを並列で処理します。

ただし、この機能は有効にさえすればすべての処理が自動的にそうなってくれるわけではなく、Hiveの個々の処理がそうなるように実装する必要があるため、すべての機能が対応しているわけではありません。 それでも、有効にしないことによるデメリットはあまりないので、有効にしておくといい機能です。

hive_vec

Vectorizationを有効にするには

set hive.vectorized.execution.enabled=true;
set hive.vectorized.execution.reduce.enabled=true;

と記述するだけです。

CBO

Cost Based Optimizerと呼ばれる機能です。 実行計画の最適化の際にテーブルの統計情報を利用するものになります。

これは主にJOINをする際の効率を最適化する仕組みす。Hiveのクエリは最終的にTezのDAGとなって実行されますが、この時より早い(DAGのツリー上で浅いところ)で可能な限り処理対象となるデータの行数を減らすことができれば、後続の処理で扱う件数やネットワークをまたいでの転送を行うデータ量を削減できるのでその分全体の処理として高速化可能になります。 例えば

SELECT 
  table1.column1, table1.column2, table2.column3, table2.column4
FROM
  table1 INNER JOIN table2 on table1.column5 = table2.column5
WHERE
  table1.column6 = 10
;

のようなクエリがあった際に、table1とtable2をJOINしてからcolumn6が10のもののみを抜き出すよりも、table1をcolumn6が10のもに絞り込んでからJOINする方が、ここの処理で扱うデータの行数が少なくなります。 このように、全体として効率的に処理を行うために順番を決めるのがOptimaizerとなります。

CBOでは、さらに各テーブルの統計情報もこの最適化に使うことになり、 例えば複数のテーブルを利用するJOINを行う際にどのような順番で結合していくのが最も効率的かを判断したDAGを生成することになります。

一例として、スタースキーマのような構造のデータベースで複数種類のファクトテーブルがあるような場合を考えます。 この時、

  • 順次JOINしていくか
  • ファクトテーブル1に関連するディメンションとファクトテーブル2に関連するディメンションをそれぞれ結合したのち、その2つを結合するか

の2つの可能性があります。 前者の場合、途中でファクトテーブルのうちどちらかが入ってしまい、それ以降のJOIN対象となるデータが大きくなりますが、後者の場合ですと、おそらく大きな結合は最後の1回だけでしょう。 これを各テーブルの行数などの統計情報から判断してくれるのがCBOの役割となります。

hive_cbo

CBOを有効にするには

set hive.cbo.enable=true;
set hive.compute.query.usigin.stats=true;
set hive.stats.fetch.column.stats=true;
set hive.statsfetch.partition.stats=true;

として、テーブルの統計情報の更新をします。

ANALYZE TABLE <テーブル名> COMPUTE STATISTICS FOR COLUMNES;

Hiveの実行エンジン

Hiveは実行エンジンの指定ができます。これは実際の処理を行う方法を決定するもので、MapReduce, Tez, Sparkから選択できます。

実行エンジンの選択は

set hive.execution.engine=mr;    -- MapReduceを使う場合
set hive.execution.engine=tez;   -- Tezを使う場合
set hive.execution.engine=spark; -- Sparkを使う場合

という1行の設定で完結します。
HiveはTezを利用することを前提とした機能がが多数存在しているので、特に理由がなければTezを使うことをお勧めします。 また、MapReduceに関しては利用するメリットがあまりなく、今後非推奨となっていくのであまり利用する理由はないでしょう。

.hiverc

最後に.hivercというものを紹介しておきます。 これはホームディレクトリに置いておくことで実行時に最初に読み込んでくれるファイルです。

今回の内容のいくつかはsetで始まるもので、これらはhiveの動作を設定するものでした。 こういった設定は毎回書くのは手間がかかります。そこで.hivercに記述しておけば起動時にあらかじめ読み込んでくれるので毎回書く必要がありません。
また、このファイルはhiveコマンド実行時に-iオプションをつけることで明示的に任意のファイルを選択できます。

終わりに

今回はHiveに関する機能をいくつか紹介しました。 実際にHiveを使っていくとこれらの機能が役立つ場面も多いかと思いますで、ぜひ使ってみてください。

明日はHive2.0以降で追加されたLLAPというものの紹介をしたいと思います。
ぜひ、お楽しみに!