Hadoopアドベントカレンダー2011 17日目 - Hive 0.8 について -

こんにちは、タムタムです。
久しぶりに記事を書きます。 Hadoopアドベントカレンダー2011の17日目という事で書かせていただきます。

私のような底辺エンジニアでも集合論の知識のみで簡単にデータ集計処理が書けるのがHiveの良いところだと思います。
試しに某ゲームのモンスター討伐のログを、数日分、全ワールド、Zoneエリア、モンスター単位で集計したら100秒くらいで結果が出ました。(その時はレコードが1億程度と少ないという事もありますが)
※ちなみに私はマイニングエンジニアではありません。

というわけでHive0.8に関する事を書きたいと思います。
Hive0.8ではBitmapインデックスやtimestamp型が追加されたというのが大きなトピックらしいです。さっそく新機能を試してみたいと思います。また細かい構文が追加されているようなので、それもおいおい試してみたいと思います。今回はtimestamp型について書きます。
ちなみに、まだリリースされていないのでソースを持ってきてビルドする必要があります。ビルドの方法は次の通りです。

svn co http://svn.apache.org/repos/asf/hive/branches/branch-0.8/ hive-0.8
cd hive-0.8
ant clean package

build/dist配下にパッケージが出来ていますので、build/distをHIVE_HOMEに設定してbin/hiveを実行します。

※(追記)Hive 0.8は12/16にリリースされてましたw

今まで日付データをhiveで扱うにはstringとして保持してWHERE句にUDFで日付に戻して計算をするとか、bigintでunixtimeとして保持したりしていました。timestamp型についてまずはどんな操作ができるのか調べてみます。

hive> show functions;

の結果を0.7と0.8で比較し、0.8で追加されたUDFを列挙しました。 (descriptionはコマンド打っても出てこなかったものがあるので書くのをやめましたw)

UDF Description
ewah_bitmap
ewah_bitmap_and
ewah_bitmap_empty
ewah_bitmap_or
from_utc_timestamp
map_keys
map_values
named_struct
timestamp
to_utc_timestamp

またJIRAのチケットによると、datetimeに関する様々なUDFがtimestampをサポートしたようです。

例えば以下のような操作ができます。
サンプルデータを1件用意します。書式はyyyy-MM-dd HH:mm:ss か yyyy-MM-dd HH:mm:ss.fffffffffのみです。

2011-12-25 09:00:00
create table ts1(t1 timestamp);
load data local inpath '/mnt/iscsi/test1/data4.txt' overwrite into table ts1;

select cast(t1 as float), cast(t1 as double) from ts1 limit 1;
1.3247712E9     1.3247712E9

select cast(t1 as bigint), cast(t1 as int), cast(t1 as boolean), cast(t1 as tinyint), cast(t1 as smallint) from ts1 limit 1;
1324771200      1324771200      true    -128    26496

select cast(t1 as string) from ts1 limit 1;
2011-12-25 09:00:00

select cast('2011-12-25 9:00:00.000000000' as timestamp) from ts1 limit1;
2011-12-25 09:00:00

select
  unix_timestamp(t1),
  year(t1),
  month(t1),
  day(t1),
  dayofmonth(t1),
  weekofyear(t1),
  hour(t1),
  minute(t1),
  second(t1),
  to_date(t1)
from ts1;
1324771200      2011    12      25      25      51      9       0       0       2011-12-25

select date_add(t1, 5), date_sub(t1, 5) from ts1 limit 1;
2011-12-30      2011-12-20

select datediff(t1, t1), datediff(t1, '2011-12-10'), datediff('2011-12-20 9:00:00', t1) from ts1 limit 1;
0       15      -5

新しいUDFも試してみましょう。

select from_utc_timestamp(t1, 'JST') from ts1 limit 1;
2011-12-25 18:00:00

select from_utc_timestamp(t1, 'UTC') from ts1 limit 1;
2011-12-25 09:00:00

select from_utc_timestamp(t1, 'PST') from ts1 limit 1;
2011-12-25 01:00:00

select to_utc_timestamp(t1, 'JST') from ts1 limit 1;
2011-12-25 00:00:00

select to_utc_timestamp(t1, 'UTC') from ts1 limit 1;
2011-12-25 09:00:00

select to_utc_timestamp(t1, 'PST') from ts1 limit 1;
2011-12-25 17:00:00

うまく動いているように見えます。が、以下のクエリを発行すると正しい結果が得られません。
SELECT句に複数記述するのはNGなのかな?

select from_utc_timestamp(t1, 'JST'), from_utc_timestamp(t1, 'UTC'), from_utc_timestamp(t1, 'PST') from ts1 limit 1;
2011-12-25 10:00:00     2011-12-25 10:00:00     2011-12-25 10:00:00

select to_utc_timestamp(t1, 'JST'), to_utc_timestamp(t1, 'UTC'), to_utc_timestamp(t1, 'PST') from ts1 limit 1;
2011-12-25 08:00:00     2011-12-25 08:00:00     2011-12-25 08:00:00

ちなみに、2011-12-25 09:00:00はどのTimeZoneの9時なのか?という事が気になると思います。チケットを見ると、「hive.time.default.timezone」こういう設定が一瞬だけあったようですが、「Removed references to a "default" timezone. All times are treated as UTC」という事で無かったことになっています。実際は上の結果を見る限り(castとか)TimeZone.getDefault()が使われているように見えます。(すいませんソース追っていません)。なのでOracleVMの場合はVMプロパティのuser.timezoneで指定できますが、システムプロパティなので他に影響を及ぼしそうで怖いです。

Javaのコードで確認したら似たような動作したので、たぶんDefaultのTimeZoneを使っていると思います。grepしたらUTCとかいっぱい出てきましたけどw
(ていうか、Hiveのソース読めよって話ですよね)

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("JST"));
System.out.println(sdf.parse("2011-12-25 09:00:00").getTime());

sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(sdf.parse("2011-12-25 09:00:00").getTime());
1324771200000
1324803600000

個人的には、時間に関する型をサポートするならSimpleDateFormatの書式をtableの設定で出来ると嬉しいんですけどね。

というわけで、timestamp型に関する事を書いてみました。
もしもネタが足りなければ列指向のRCFileとSequenceFileの性能の違いやRCFileをJavaののコードで作る方法あたりを書きたいと思います。

間違いを見つけたらどんどん指摘してくれると嬉しいです(・ω・)ノ

新しいサイトもよろしくお願いします!