CUBE SUGAR CONTAINER

技術系のこと書きます。

CentOS7 で Apache Hive を使ってみる

今回は Apache Hadoop 上で動作する MapReduce アプリケーションの一つ Apache Hive を使ってみる。 Apache Hive を使うと Hadoop/HDFS の上で HiveQL という SQL のサブセットが使えるようになる。 実行したクエリは MapReduce のジョブに変換されて Hadoop クラスタで分散並列処理されることから高スループットが得られる。

ただし、MapReduce アプリケーションのご多分に漏れずレイテンシーはでかい。 ようするに一つ一つのクエリの実行自体には時間がかかってしまう。 また、一度追加したレコードについては基本的に更新したり削除することができない。 それらの特性から、オンライントランザクション処理 (OLTP) のような用途には全く向いていない。 代わりに、どんどん一方的にデータが蓄積されていくような状況で後からバッチとかで集計するような用途には向いていると思う。

そんな Apache Hive を今回は CentOS7 上に構築して使ってみる。 今回も Hadoop ディストリビューションは使わず、OSS のリリースパッケージをそのままインストールする。 その方が、おそらく挙動や設定について多くの知見が得られると思うので。 そして、Apache Hive は Apache Hadoop/HDFS 上で動作する。 そのため、今回使う環境は以下のエントリにもとづいて Apache Hadoop が疑似分散モードでセットアップ済みなことを想定する。

blog.amedama.jp

上記のエントリにもあるけど、今回使った環境は次の通り。

$ cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)
$ uname -r
3.10.0-514.16.1.el7.x86_64
$ bin/hadoop version
Hadoop 2.8.0
Subversion https://git-wip-us.apache.org/repos/asf/hadoop.git -r 91f2b7a13d1e97be65db92ddabc627cc29ac0009
Compiled by jdu on 2017-03-17T04:12Z
Compiled with protoc 2.5.0
From source with checksum 60125541c2b3e266cbf3becc5bda666
This command was run using /home/vagrant/hadoop-2.8.0/share/hadoop/common/hadoop-common-2.8.0.jar

インストールする

インストール方法については次のドキュメントを参照した。 ただし、Apache Hive 2.x 系についてはここに書かれている内容だけではエラーになってしまった。 そのため公式サイト以外にも調べて必要な手順を足している。

GettingStarted - Apache Hive - Apache Software Foundation

AdminManual Installation - Apache Hive - Apache Software Foundation

下準備

まずはパッケージをダウンロードするために wget をインストールしておく。

$ sudo yum -y install wget

続いては環境変数 HADOOP_HOME に Apache Hadoop のインストール先ディレクトリを指定しておく。

$ export HADOOP_HOME=~/hadoop-2.8.0

続いては Apache Hive のパッケージをダウンロードする。 ミラーサイトは次の通りなので、好きなところを選ぼう。

www.apache.org

ただし、現状で Apache Hive には 1.x 系と 2.x 系という二つのメジャーバージョンが併用されている。 軽く調べた感じ、まだ 1.x 系の方が使われているっぽいかな? ただ、2.x 系には 1.x 系にない機能が色々とあるみたい。 ちなみに、どうやら Apache Hive は最新の安定版リリース以外ダウンロードサイトに置いていないようだ。

バージョン 1.x を使う場合

もしバージョン 1.x 系を使うときは、こうする。

$ wget http://ftp.riken.jp/net/apache/hive/hive-1.2.2/apache-hive-1.2.2-bin.tar.gz
$ tar xf apache-hive-1.2.2-bin.tar.gz
$ cd apache-hive-1.2.2-bin
バージョン 2.x を使う場合

バージョン 2.x 系を使うときは、こう。

$ wget http://ftp.riken.jp/net/apache/hive/hive-2.1.1/apache-hive-2.1.1-bin.tar.gz
$ tar xf apache-hive-2.1.1-bin.tar.gz
$ cd apache-hive-2.1.1-bin

セットアップする

どちらのメジャーバージョンを使うときも同じ手順でセットアップできるようだ。 ただし 2.x 系については 1.x 系で不要な操作も必ずしなければいけないようになっている。

まずは HDFS に Hive のデータ置き場を作る。

$ $HADOOP_HOME/bin/hadoop fs -mkdir -p /user/hive/warehouse
$ $HADOOP_HOME/bin/hadoop fs -chmod g+w /user/hive/warehouse

同様にテンポラリディレクトリも必要なので作っておこう。

$ $HADOOP_HOME/bin/hadoop fs -mkdir -p /tmp
$ $HADOOP_HOME/bin/hadoop fs -chmod g+w /tmp

続いては Apache Hive がメタデータを入れておく RDBMS を準備する。 これには MySQL とか PostgreSQL とかも使えるけど、デフォルトでは Apache Derby という Java で書かれた組み込みのものが使われる。

$ bin/schematool -dbType derby -initSchema --verbose
...(snip)...
Initialization script completed
schemaTool completed

これで下準備はおわり。

使ってみる

続いては、実際に Apache Hive を使ってみることにしよう。 それにはまず hive というコマンドを起動する。

$ bin/hive

すると、次のように HiveQL を入力するためのシェルが起動する。

hive>

ちなみに、このとき 2.x 系を使っていると次のような警告が出る。 どうやら 2.x 系では MapReduce で動作させるのは非推奨となっていて、代わりに Spark とか Tez の上で動かせってことらしい。 ここらへんはまだよく分かっていないけど、おそらくレイテンシーの大きさを改善するための試みな気がしている。

Hive-on-MR is deprecated in Hive 2 and may not be available in the future versions. Consider using a different execution engine (i.e. spark, tez) or using Hive 1.X releases.

シェルの使い方

基本的には MySQL なんかのシェルを使うのと似たような感じで操作できる。 例えばデータベースの一覧を得るには SHOW DATABASES コマンドが使える。

hive> SHOW DATABASES;
OK
default
Time taken: 0.011 seconds, Fetched: 1 row(s)

今操作しているデータベースを表示したいときは、こうする。

hive> set hive.cli.print.current.db=true;

シェルの中に表示されるようになった。

hive (default)>

データベースを作る

データベースを新しく作るには CREATE DATABASE コマンドを使う。

hive (default)> CREATE DATABASE mydb;
OK
Time taken: 0.106 seconds

使うには USE コマンドを。 ここらへんは MySQL とほとんど同じ。

hive (default)> USE mydb;
OK
Time taken: 0.038 seconds

hive (mydb)>

テーブルを作る

続いてはテーブルを作ってみる。 ここも至って普通の SQL が使える。 代理キーを入れてみたけど、特に自動生成してくれるような機能はなさそうかな・・・?

hive (mydb)> CREATE TABLE users (
           >   id INT,
           >   name STRING,
           >   age INT
           > );
OK
Time taken: 0.068 seconds

SHOW TABLES で確認すると、ちゃんとテーブルができている。

hive (mydb)> SHOW TABLES;
OK
users
Time taken: 0.022 seconds, Fetched: 1 row(s)

続いてはレコードを追加してみる。 さっきまでの処理は単に RDB にメタデータを追加するだけで済んでいたのかすぐに終わったけど、この処理には結構かかる。 がっつり MapReduce のジョブに変換されているみたいだ。

hive (mydb)> INSERT INTO users VALUES (1, 'Alice', 20);
...(snip)...
Total MapReduce CPU Time Spent: 1 seconds 860 msec
OK
Time taken: 31.601 seconds

SELECT で追加されたレコードを確認してみよう。

hive (mydb)> SELECT * FROM users;
OK
1  Alice   20
Time taken: 0.22 seconds, Fetched: 1 row(s)

ちなみに、クエリがどう処理されているかを詳しく見るには EXPLAIN を先頭につけるらしい。

hive (mydb)> EXPLAIN SELECT * FROM users;
OK
STAGE DEPENDENCIES:
  Stage-0 is a root stage

STAGE PLANS:
  Stage: Stage-0
    Fetch Operator
      limit: -1
      Processor Tree:
        TableScan
          alias: users
          Statistics: Num rows: 1 Data size: 10 Basic stats: COMPLETE Column stats: NONE
          Select Operator
            expressions: id (type: int), name (type: string), age (type: int)
            outputColumnNames: _col0, _col1, _col2
            Statistics: Num rows: 1 Data size: 10 Basic stats: COMPLETE Column stats: NONE
            ListSink

Time taken: 0.086 seconds, Fetched: 17 row(s)

続いては複数のテーブルを連結してみる。 試しにユーザのメールアドレスを想定したテーブルを作ってみよう。

hive (mydb)> CREATE TABLE emails (
           >   id INT,
           >   address STRING
           > );
OK
Time taken: 0.062 seconds

hive (mydb)> INSERT INTO emails VALUES (1, 'alice@example.jp');
...(snip)...
OK
Time taken: 18.881 seconds

テーブル同士を連結 (JOIN) するときも一般的な RDBMS と同じようにできる。 ただし、この処理も実行には時間がかかる。

hive (mydb)> SELECT *
           > FROM users
           > JOIN emails ON users.id = emails.id
           > WHERE name like 'Alice';
...(snip)...
OK
1  Alice   20 1  alice@example.jp
Time taken: 31.934 seconds, Fetched: 1 row(s)

ところで、これまで見てきたテーブルなどのデータは HDFS 上にどのようにして格納されているのだろう? 見ると、次のようにテーブルごとでディレクトリになってその中にファイルが入っている。

$ $HADOOP_HOME/bin/hdfs dfs -ls -R /user/hive/warehouse
drwxrwxr-x   - vagrant supergroup          0 2017-05-22 21:35 /user/hive/warehouse/mydb.db
drwxrwxr-x   - vagrant supergroup          0 2017-05-22 21:35 /user/hive/warehouse/mydb.db/emails
-rwxrwxr-x   1 vagrant supergroup         19 2017-05-22 21:35 /user/hive/warehouse/mydb.db/emails/000000_0
drwxrwxr-x   - vagrant supergroup          0 2017-05-22 21:33 /user/hive/warehouse/mydb.db/users
-rwxrwxr-x   1 vagrant supergroup         11 2017-05-22 21:33 /user/hive/warehouse/mydb.db/users/000000_0

さらにファイルの中身を見てみると・・・なんと、ただのテキストファイルになっている。 こんなデータ構造でも上手くいくのは Hadoop クラスタと HDFS の賜物といえるだろう。 HDFS で各ノードに分散配置された細切れのファイルを Hadoop クラスタが分散並列実行するために高いスループットが得られる。

$ $HADOOP_HOME/bin/hdfs dfs -cat /user/hive/warehouse/users/000000_0
1Alice20

テーブルを削除するには DROP TABLE を使う。

hive (mydb)> DROP TABLE users;
OK
Time taken: 0.946 seconds
hive (mydb)> DROP TABLE emails;
OK
Time taken: 0.113 seconds

テーブルを削除すればディレクトリごとファイルもなくなる。

$ $HADOOP_HOME/bin/hdfs dfs -ls -R /user/hive/warehouse
drwxrwxr-x   - vagrant supergroup          0 2017-05-22 12:39 /user/hive/warehouse/mydb.db

データ構造を CSV にする

先ほど見た通り Apache Hive が扱うデータ構造は、デフォルトではただのテキストファイルになっている。 このことから、データ構造を例えば CSV にすることもできる。

テーブルを作るときのオプションとしてカンマ区切りになるよう指定しよう。

hive (mydb)> CREATE TABLE users (
           >   id INT,
           >   name STRING,
           >   age INT
           > )
           > ROW FORMAT DELIMITED
           > FIELDS TERMINATED BY ','
           > STORED AS TEXTFILE;
OK
Time taken: 0.079 seconds

もちろん、こうしておけば外部から CSV を読み込むこともできる。 例えば、次のようなファイルを用意しよう。

$ cat << 'EOF' > /tmp/users.csv
1,Alice,20
2,Bob,30
3,Carol,10
EOF

そして Hive で LOAD DATA を使って、それを読み込む。

hive (mydb)> LOAD DATA LOCAL INPATH '/tmp/users.csv' INTO TABLE users;
Loading data to table mydb.users
Table mydb.users stats: [numFiles=1, totalSize=31]
OK
Time taken: 0.223 seconds

データがちゃんと読み込まれている。

hive (mydb)> SELECT * FROM users WHERE age < 25;
OK
1  Alice   20
3  Carol   10
Time taken: 0.141 seconds, Fetched: 2 row(s)

そして、HDFS 上のデータの中身もこの通り。

$ $HADOOP_HOME/bin/hdfs dfs -ls -R /user/hive/warehouse | grep users.csv
-rwxrwxr-x   1 vagrant supergroup         31 2017-05-22 21:40 /user/hive/warehouse/mydb.db/users/users.csv
$ $HADOOP_HOME/bin/hdfs dfs -cat /user/hive/warehouse/mydb.db/users/users.csv
1,Alice,20
2,Bob,30
3,Carol,10

Vagrantfile

上記のセットアップを手動でやるのは大変なので Vagrant で自動化してみた。 使い方は次の通り。

$ git clone https://gist.github.com/d89890c1e9874f5e15f8cff9311544a5.git hadoop-cluster-with-hive
$ cd hadoop-cluster-with-hive
$ sh boot.sh

あとはマスターノードにログインして色々とやる。

$ vagrant ssh master

まとめ

今回は CentOS7 に Apache Hive をインストールして使ってみた。 Apache Hive を使うことで Hadoop/HDFS 上でスケーラブルなデータストアを構築できる。 構築したデータストアは HiveQL という SQL のサブセットを通して操作できる。 HiveQL は、実行されると MapReduce のジョブに変換された上で Hadoop クラスタ上で並列分散実行される。 そのためレイテンシーは大きいものの高いスループットが期待できるようだ。

今回も次の本が理解の役に立った。

Hadoop徹底入門 第2版 オープンソース分散処理環境の構築

Hadoop徹底入門 第2版 オープンソース分散処理環境の構築