SqoopでMySQLからHadoop+Hiveにパーテション作成しながらデータ転送,ベンチマーク付き

※ここで言うパーティションとは,MySQLパーティションではなく,Hiveのパーティションです

最初,HiveのALTER TABLE ADD PARTITIONって,既に存在するHiveテーブルにあとからパーティションを追加設定できるのかと思ったら,さすがにそんなことはできなくて,こんなエラーを出してしまいました.

$ hive -e 'ALTER TABLE game_db ADD PARTITION (dt="2013-11-08")
FAILED: Error in metadata: table is not partitioned but partition spec exists: {dt=2013-11-08}

DROP TABLEして,Sqoopを呼ぶスクリプトを叩いてテーブルごと作り直しました.

作ったシェルスクリプトはこんな感じです.

#!/bin/sh
jdbc="jdbc:mysql://mysqlserver:mysqlport/GameDB?zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false"
auth="--username mysqladmin --password mysqlpass"

## Common Setting
date=2013-11-08"
opt="--hive-import --hive-drop-import-delims --fields-terminated-by \001 --hive-partition-key dt --hive-partition-value $date"
table=game_db
split=member_id
query="\"SELECT * FROM $table WHERE \$CONDITIONS\""
sqoop import --connect "$jdbc" $auth --query "$query" --split-by "$split" --target-dir "$table" --hive-table "$table" $opt

わざわざfree form queryを用意しているのは,別なスクリプトで日時で範囲指定したりする必要があったからです.

このスクリプトを実行して,HDFSに転送してからHiveに入れるのにかかる時間はこんな感じです.

$ time ./import.sh

(snip)

INFO mapreduce.ImportJobBase: Transferred 1.3182 GB in 276.7211 seconds (4.8779 MB/sec)
INFO mapreduce.ImportJobBase: Retrieved 49999999 records.

(snip)

real    4m49.467s
user    0m26.974s
sys     0m1.399s

データ数が5千万レコードくらいで実行時間は5分くらい.


Hiveのでレコードカウントさせると,30秒くらいかかりました.

$ hive -e 'SELECT count(*) FROM game_db'

(snip)

MapReduce Jobs Launched: 
Job 0: Map: 4  Reduce: 1   Cumulative CPU: 61.72 sec   HDFS Read: 1415391770 HDFS Write: 9 SUCCESS
Total MapReduce CPU Time Spent: 1 minutes 1 seconds 720 msec
OK
49999999
Time taken: 33.615 seconds

中身をチラ見しますとこんな感じです.

$ hive -e 'select * from game_db limit 3'

OK
53013   3236    8365    183     8.9     1       774770  8889    83      1948561 3       55      2013-11-08
78593   9419    1633    8382    0.1     0       533277  23301   42      1307030 37      2       2013-11-08
93127   4224    1671    9872    7.2     0       239504  75619   83      1414712 75      40      2013-11-08

データはランダムに作ってあり,最後のカラムがパーティションキーです.

ファイル容量は圧縮して13Gバイトくらい.

$ hdfs dfs -du -h /user/hive/warehouse/game_db/dt=2013-11-08/\*
0  /user/hive/warehouse/mgs_fishing_log/dt=2013-11-08/_SUCCESS
335.4 M  /user/hive/warehouse/game_db/dt=2013-11-08/part-m-00000.deflate
338.2 M  /user/hive/warehouse/game_db/dt=2013-11-08/part-m-00001.deflate
338.1 M  /user/hive/warehouse/game_db/dt=2013-11-08/part-m-00002.deflate
338.1 M  /user/hive/warehouse/game_db/dt=2013-11-08/part-m-00003.deflate

日付を変えて4日分くらい作ります.

$ hive -e 'SELECT count(*) from game_db' 
OK
199999996
Time taken: 42.178 seconds

ここでまた,日付を指定して1日分だけカウント.

$ hive -e "SELECT count(*) FROM mgs_fishing_log WHERE dt='2013-11-10'"

OK
49999999
Time taken: 34.556 seconds

確かにデータ自体が1日分しか無い時の実行時間に戻った気がします.

でも日付が入ったとはいえ,文字列だから範囲指定ができないよね,と思ったのですが,辞書式に比較されるのか,普通に出来ました.

$ hive -e "SELECT count(*) FROM mgs_fishing_log WHERE dt>='2013-11-10'"

OK
99999998
Time taken: 40.863 seconds

賢い.


ただしSqoopでexportする時は

#!/bin/sh
jdbc="jdbc:mysql://mysqlserver:mysqlport/GameDB"
auth="--username mysqladmin --password mysqlpass"

## Common Setting
date=2013-11-08"
opt="--fields-terminated-by \001 --hive-partition-key dt --hive-partition-value $date"
src=/user/hive/warehouse/game_db
dst=GAME_DB
sqoop export --connect "$jdbc" $auth --table "$dst" --export-dir "$src" $opt

とはできなくて,

 ERROR tool.ExportTool: Encountered IOException running export job: java.io.FileNotFoundException: Path is not a file: /user/hive/warehouse/game_db/dt=2013-11-08

エラーになります.
パーティションをサポートしていないようなのですが,しょせんはHDFSの中のファイルなので,ディレクトリまで指定してやればイケます.

#!/bin/sh
jdbc="jdbc:mysql://mysqlserver:mysqlport/GameDB"
auth="--username mysqladmin --password mysqlpass"

## Common Setting
date=2013-11-08"
opt="--fields-terminated-by \001 --hive-partition-key dt --hive-partition-value $date"
src="/user/hive/warehouse/game_db/dt=$date"
dst=GAME_DB
sqoop export --connect "$jdbc" $auth --table "$dst" --export-dir "$src" $opt

(実際にはMicrosoft SQL Serverにexportしているのでコネクションストリングなんかが若干違うのですが,だいたいこんな感じでMySQLでもイケるでしょう)