なんとな~くしあわせ?の日記

「そしてそれゆえ、知識そのものが力である」 (Nam et ipsa scientia potestas est.) 〜 フランシス・ベーコン

Apache PigでZipファイルをロード

github.com

経緯

仕事でHadoopを使うとき、gzip/bzipは標準で読み込めるがzipは読み込めなかった。困る。

Hadoopの本流でzipファイルを読む機能がマージされてないまま放置されていることに気づく

1. It does not split the zip files efficiently. This is because there is no way in Java to construct a zip input stream that permits random seeks given a zip entry name.
2. Java's handling of large zip file is not robust.

と、言われてます。1はよくわかりませんが、2については How to iterate zip file records (java.util.zip.ZipFile, java.util.zip.ZipInputStream) - Java Performance Tuning Guide に書かれているように、Java7からは2GB超えファイルにも標準で対応しています。

Map/Reduce

さっきのチケットの中で、Map/Reduceを拡張して読めるようにしてるブログを見つけました。

あと、同じことやってる人を見つけた

つまりはApache PigのLoad関数はHadoopのInputFormatクラスを使用している。それはそれぞれのレコードに対してRecordReaderをとり、そしてTuple(かそれ以外の何か)に変換している。なので、zip圧縮されたファイルを読みたいのならば、どうしても自前のInputFormat/RecordReaderを書く必要がある。上記のサイトはすでにそれを用意してくれている。

ひしだまさんのサイトでLoadFuncの拡張方法を知る

Javaと言えばひしだまさん。

ここでLoadFuncについて学んだらすぐにできた。

使い方

  • mvn packageなどを実行してjarファイルを作ってpigスクリプトに追加
%declare ZIPLOADER 'com.cotdp.pigudf.ZipLoader';

REGISTER target/com-cotdp-hadoop-1.0-SNAPSHOT.jar

-- 全ファイル取得
A = LOAD 'src/test/resources/zip-01.zip' USING $ZIPLOADER('');
DUMP A;

-- 改行を区切りにしてbagとして取得
A = LOAD 'src/test/resources/zip-01.zip' USING $ZIPLOADER('\r\n') AS (raw:bytearray);
B = FOREACH A GENERATE FLATTEN($0) AS (raw:chararray);
DUMP B;

今はほんとに中身を出力するだけ