Hive on TezにおいてDISTRIBUTE BY指定したクエリのFile Mergeの挙動
Hive on Tezにおいて、DISTRIBUTE BYを指定したクエリが、File Mergeが有効化されていたことによって想定外の挙動になった。 Hiveのバージョンは、3.1.3でORC形式のテーブルを想定している。また、他のバージョンでは修正されている可能性はある。
まず、DISTRIBUTE BYの挙動からおさらいしておくと、DISTRIBUTE BYに指定したカラムについて、カラムの値が同じであれば同じReducerで処理されることを保証するものである。また、競合を避けるためにReducerの単位で別のファイルに結果が書かれることになる。 つまりは、DISTRIBUTE BYで指定したカラムについて、同じ値を持つレコードは、必ず同じファイルに書かれるということが保証される。
LanguageManual SortBy - Apache Hive - Apache Software Foundation
Sort By, Order By, Distribute By, and Cluster By in Hive - SQLRelease
これはHive on TezでもHive on MapReduceでも同じことである。
HiveではORDER BYをつけることでデータ全てが一貫してソートされているグローバルソートを実現できるが、全てのデータを1箇所でソートする必要があるため、当然Reducerの数も1つに制限されてしまう。書き込み先のファイルが1つになるため、データサイズが大きい場合に容易にボトルネックとなる。 そこで、グローバルソートは不要だが、特定のカラム単位では一貫したソート結果が欲しいと言うようなケースで、このDISTRIBUTE BYとSORT BYを組み合わせることで、Reducerごとのソートを可能とすることができる。
しかし、Small FIle Problemを避けるためにhive.merge.tezfiles
がtrueになっている環境下においては、この挙動が変わってしまうと言うことが観測された。
hive.merge.tezfiles
をtrueにしていると、そうではない場合のFile MergeのためのReducerが追加される。
ここまでは良いが、Hive3.1.3では、その前段までは保持されていたDISTRIBUTE BYのコンテキストが、File Mergeの時点では失われてしまい、完全に無視されてファイルマージが行われてしまうということが分かった。
つまり、ファイルマージされる前まではReducer単位でのソートができていたが、その後ファイルサイズを最適化するために、前段のファイルを分割/結合して、マージすることになる。
ファイル単位での単なる結合であれば、これはReducer毎のソートを維持したまま達成可能である。しかし分割が行われた時点でこの保証は無くなってしまう。
ORCのストライプレベルでのファイルマージを制御するhive.merge.orcfile.stripe.level
フラグをfalseにした場合でも、やはりファイル分割が行われてしまった。
結果として、Reducer毎のソートは無効になってしまい、異なるファイルに同じDISTRIBUTE BYカラムを持つレコードが分割して含まれてしまうと言う問題が発生する。
こういった問題を避けつつ、Small File Problemも一定数解決したい場合には、
set hive.merge.tezfiles=false; set hive.exec.reducers.max=xxx;
のように指定を行い、ファイルマージを回避しつつ、Reducerの数を減らすことでなるべくパフォーマンスを落とさずにファイル数を少なくすると言うチューニングが考えられる。