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でもイケるでしょう)