ファイルを直接読み込んで集計する

こんにちは。マーケティングプロダクト開発部の中村です。今回は大量のデータを対象に集計できる Hive の使い方について説明しようと思います。

前提

私が所属しているマーケティングプロダクト開発部では広告配信も行っています。その広告配信では大量のアクセスログを蓄積しています。通常ですとそのログは Amazon Redshift で簡単に集計できます。しかし、ログファイルを直接集計しなければならない場合が稀にあります。その際に使用しているのが Amazon EMR です。今回は Hive を用いてその集計を手元の端末で試してみます。

インストール

まずは動作環境を作るために Hive をインストールします。

brew install hive

集計する前の準備

Hive は任意のディレクトリを作業ディレクトリとすることができます。まず、その作業ディレクトリを作成し、そのディレクトリに移動しておきます。

mkdir -p /tmp/cookpad/logs
cd /tmp/cookpad

次に、その作業ディレクトリで使用する Schema の種類を指定する必要があります。今回は資料でデフォルトで使われてる derby を選択します。

schematool -initSchema -dbType derby

ここまでの作業で、ローカルで起動させるための準備ができました。次に、実際に起動させてみます。

hive

Hive のコンソールが立ち上がれば成功です。

集計してみる

次にサンプルをもとに簡単に集計します。今回は Nginx のアクセスログをサンプルとして集計してみようと思います。具体的には以下の内容のファイルを /tmp/cookpad/logs/nginx.log として、先程作成したディレクトリ以下に保存します。

172.17.0.1 - - [14/Jul/2017:07:48:37 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"
172.17.0.1 - - [14/Jul/2017:07:48:38 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"
172.17.0.1 - - [14/Jul/2017:07:48:38 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"
172.17.0.1 - - [14/Jul/2017:07:49:19 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"
172.17.0.1 - - [14/Jul/2017:07:49:40 +0000] "GET /hoge HTTP/1.1" 404 571 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"
172.17.0.1 - - [14/Jul/2017:07:49:42 +0000] "GET /hoge HTTP/1.1" 404 571 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"
172.17.0.1 - - [14/Jul/2017:07:49:51 +0000] "GET /piyo HTTP/1.1" 404 571 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"
172.17.0.1 - - [14/Jul/2017:07:49:52 +0000] "GET /piyo HTTP/1.1" 404 571 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"
172.17.0.1 - - [14/Jul/2017:07:49:53 +0000] "GET /piyo HTTP/1.1" 404 571 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"

次にそのファイルを集計するクエリを用意します。なお Hive は起動時にクエリをファイルから読み込めるため、ファイルに書いた方が後から参照できて便利です。具体的には以下の内容で /tmp/cookpad/sample.q として保存します。

drop table nginx_logs;

create external table nginx_logs (
    remote_addr string
    , remote_user string
    , time_local string
    , method string
    , path string
    , protocol string
    , status int
    , body_bytes_sent int
    , http_referer string
    , http_user_agent string
)
row format serde
  'org.apache.hadoop.hive.serde2.RegexSerDe'
with serdeproperties (
    "input.regex" = "([0-9\\.]+) - ([^ ]*) \\[([^\\]]*)\\] \"([^ ]*) ([^ ]*) ([^ ]*)\" ([0-9]*) ([0-9]*) \"(.*)\" \"(.*)\""
)
location
    'file:///tmp/cookpad/logs'
;

select
    *
from
    nginx_logs
where
    path = '/hoge'
;

その保存したファイルを指定して Hive を起動します。

cd /tmp/cookpad/
hive -f /tmp/cookpad/sample.q

クエリが実行されてアクセスログが表示されれば成功です。

UDF を書いて Hive に組み込む

Hive には様々な関数が組み込まれています。通常の集計ではその関数で充分なのですが、時折複雑な条件で集計したくなります。そのようなときは関数を自作して組み込んで使用することができます。

実際に関数を組み込んで集計してみます。ただし、少し準備することが多いので事前にコードは GitHub に用意しておきました。以下のリポジトリを任意の場所に clone してください。

https://github.com/devisualy/udf

そのクローンした場所に移動してビルドします。具体的には以下のコマンドを実行します。

mvn package

ビルド成功すると target/devisualy_udf.jar ができるはずなので Hive の作業ディレクトリに配置します。

cp target/devisualy_udf.jar /tmp/cookpad/devisualy_udf.jar

Hive を起動して組み込んでみます。

cd /tmp/cookpad
hive
hive> ADD JAR /tmp/cookpad/devisualy_udf.jar;
hive> CREATE TEMPORARY FUNCTION converter as 'devisualy.Converter';

OK のような表示が出れば成功しています

UDF を使って集計する

上記までで Hive 上で自作の関数を使う準備は整いました。次に実際にクエリを投げてその関数を使ってみます。具体的には以下のようなクエリを Hive のコンソール上で実行します。

select method, converter(method) from nginx_logs limit 1;

クエリ内で同じ method を参照していますが converter という関数の戻り値が method とは異なっているのを確認できます。

これまでの作業でできるようになったこと

上記までの作業で以下のことができるようになりました。

  • 集計対象ファイルを Hive で読み込めるようになった
  • そのファイルに対してクエリを投げられるようになった
  • そのクエリから自作の関数を呼べるるようになった

その中でも自作の関数を呼べるようになったのは強力です。クエリだけではなかなか表現しきれないビジネスロジックを表現できます。また UDF ファイルを共有することにより他人がその表現を簡単に流用することができます。

まとめ

Hive の基本的な使い方について説明しました。前提にも書きましたが、私が所属しているマーケティングプロダクト開発部では広告配信も行っているため、大量のアクセスログを蓄積しています。そのログに対してローカルで Hive を使うということは無く Amazon EMR を使って高速にログを集計しています。

AWS には、似たようなことが簡単に可能な Amazon Athena というサービスもあります。しかし、集計対象のデータが大きいと料金が高くなるため Amazon EMR を使ったほうが良い場合もあるかと思います。

これらの技術により、大量のデータを高速にかつ簡単に集計できるようになりました。次はその集計結果をどのように活用できるかです。いつかログからユーザーを想像できるようになれればいいなと思っています。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/