E-MapReduceでHiveの動的パーティションについて

Alibaba CloudのE-MapReduceを利用し、SELECT文の実行は、テーブルに含まれる全データをスキャンします。WHERE句を指定し、特定の条件に合致するデータを抽出する場合、大量のデータを読み込んだにも関わらず、ごく一部のデータしか利用しないケースが想定されます。例えば、時系列にならんだログデータを集計する場合、特定の日にちのデータのみを集計したい時に、すべてのデータの日にちのデータをスキャンするのは明らかに無駄だと言えます。

そこで、特定の条件で分割して、パーティションとの単位でデータを保存しておく方法が推薦されます。今回の記事は、E-MapReduceでHiveの動的パーティションの利用方法について、皆さんにご紹介させて頂きたいと考えております。

<!--more-->
<ul style="list-style-type: square;">
<li><span style="font-size: 14pt;"><strong>前提</strong></span></li>
</ul>
<ul style="list-style-type: disc;">
 	<li>EMR-3.16.0</li>
 	<li>クラスタータイプは Hadoop</li>
 	<li>ハードウェア構成(Header)はecs.sn1ne.2xlargeを1台</li>
 	<li>ハードウェア構成(Worker)はecs.sn1ne.2xlargeを3台</li>
</ul>
<pre class="EnlighterJSRAW"># cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core) 
# uname -r
3.10.0-693.2.2.el7.x86_64
# hive --version
Hive 2.3.3
Git git://build.emr.com/root/sl/hive/newhb/emr-hive -r e2f827081ca7c8faa3d28b8fd2e7ab74919a1744
Compiled by root on Sat Sep 8 09:43:53 CST 2018
From source with checksum 6c51a2a446502e34b0de1038c17616cd
# hadoop version
Hadoop 2.7.2
Subversion http://gitlab.alibaba-inc.com/soe/emr-hadoop.git -r d2cd70f951304b8ca3d12991262e7e0d321abefc
Compiled by root on 2018-11-30T09:31Z
Compiled with protoc 2.5.0
From source with checksum 4447ed9f24dcd981df7daaadd5bafc0
This command was run using /opt/apps/ecm/service/hadoop/2.7.2-1.3.2/package/hadoop-2.7.2-1.3.2/share/hadoop/common/hadoop-common-2.7.2.jar</pre>
<ul style="list-style-type: square;">
 	<li><span style="font-size: 14pt;"><strong>Hiveの動的パーティション</strong></span></li>
</ul>
まず、<a href="https://jp.alibabacloud.com/help/doc-detail/50452.htm">ossutil</a>でデータソースのバケットとディレクトリの内容を確認します。
<pre class="EnlighterJSRAW" data-enlighter-language="shell">
# ossutil ls oss://dyn-part-test/
LastModifiedTime                   Size(B)  StorageClass   ETAG                                  ObjectName
2019-02-21 15:06:44 +0800 CST            0      Standard   D41D8CD98F00B204E9800998ECF8427E      oss://dyn-part-test/input/
2019-02-21 15:07:39 +0800 CST          113      Standard   4E4E83C9B653C3D39715049D8F7231EA      oss://dyn-part-test/input/foo.txt</pre>
使った入力データ（foo.txt）の中身は下記となります。
<pre class="EnlighterJSRAW" data-enlighter-language="null">10,20120101,100,10
10,20120101,101,20
11,20120102,100,30
11,20120102,101,40
12,20120103,100,50
12,20120103,101,60
</pre>
次は、foo.txtを参照する外部テーブルと外部ORCテーブルを作ります。なぜORC形式を使うかというのは、Hive に最適化されたカラム志向型のデータフォーマットになっており高速に動作する。 いくつかの<a href="https://jp.hortonworks.com/blog/5-ways-make-hive-queries-run-faster/">資料</a>を見ると、この ORC ファイルを使うのが Hive のベストプラクティスのようです。

```sql
CREATE EXTERNAL TABLE input_tab (
pid INT, 
dt INT, 
sid INT, 
sm INT
)
ROW FORMAT DELIMITED 
FIELDS TERMINATED BY ',' 
LINES TERMINATED BY '\n'
LOCATION 'oss://dyn-part-test/input/';

CREATE EXTERNAL TABLE output_tab (
pid INT, 
sm INT
)
PARTITIONED BY(
sid INT, 
dt INT
)
ROW FORMAT DELIMITED 
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION 'oss://dyn-part-test/output/';
```
ここからポイントですが、データ挿入（OVERWRITEの代わりにINTOを指定することが可能です。）の際に、パーティションキーの値を明記すれば、静的な振り分けになりますが、値を記述しなければ、動的に振り分けられます。

```sql
SET hive.exec.dynamic.partition = true;
SET hive.exec.dynamic.partition.mode = nonstrict;
 
INSERT OVERWRITE TABLE output_tab PARTITION (sid, dt)
SELECT pid, sm, sid, dt
FROM input_tab;
```
再度バケットとディレクトリを確認すると、テーブルデータがパーティションごとに格納されるのが分かりました。
<pre class="EnlighterJSRAW" data-enlighter-language="shell"># ossutil ls oss://dyn-part-test/output/
LastModifiedTime                   Size(B)  StorageClass   ETAG                                  ObjectName
2019-02-22 16:22:04 +0800 CST            0      Standard   D41D8CD98F00B204E9800998ECF8427E      oss://dyn-part-test/output/
2019-02-22 16:22:02 +0800 CST            0      Standard   D41D8CD98F00B204E9800998ECF8427E      oss://dyn-part-test/output/sid=100/
2019-02-22 16:22:03 +0800 CST            0      Standard   D41D8CD98F00B204E9800998ECF8427E      oss://dyn-part-test/output/sid=100/dt=20120101/
2019-02-22 16:22:03 +0800 CST          232      Standard   114F0402770283A9CAB84A8F0F4C864C      oss://dyn-part-test/output/sid=100/dt=20120101/000000_0
2019-02-22 16:22:03 +0800 CST            0      Standard   D41D8CD98F00B204E9800998ECF8427E      oss://dyn-part-test/output/sid=100/dt=20120102/
2019-02-22 16:22:03 +0800 CST          245      Standard   444A0B2323EDB861D5142F6E7C5FE222      oss://dyn-part-test/output/sid=100/dt=20120102/000000_0
2019-02-22 16:22:02 +0800 CST            0      Standard   D41D8CD98F00B204E9800998ECF8427E      oss://dyn-part-test/output/sid=100/dt=20120103/
2019-02-22 16:22:03 +0800 CST          245      Standard   C036552D01C4835AEA681657C5BF808F      oss://dyn-part-test/output/sid=100/dt=20120103/000000_0
2019-02-22 16:22:02 +0800 CST            0      Standard   D41D8CD98F00B204E9800998ECF8427E      oss://dyn-part-test/output/sid=101/
2019-02-22 16:22:03 +0800 CST            0      Standard   D41D8CD98F00B204E9800998ECF8427E      oss://dyn-part-test/output/sid=101/dt=20120101/
2019-02-22 16:22:03 +0800 CST          245      Standard   7421C8FC9DB4BA94646F77CAADE17E56      oss://dyn-part-test/output/sid=101/dt=20120101/000000_0
2019-02-22 16:22:03 +0800 CST            0      Standard   D41D8CD98F00B204E9800998ECF8427E      oss://dyn-part-test/output/sid=101/dt=20120102/
2019-02-22 16:22:03 +0800 CST          245      Standard   FCA3F020AF70EB6B9AA64616EACA639A      oss://dyn-part-test/output/sid=101/dt=20120102/000000_0
2019-02-22 16:22:03 +0800 CST            0      Standard   D41D8CD98F00B204E9800998ECF8427E      oss://dyn-part-test/output/sid=101/dt=20120103/
2019-02-22 16:22:03 +0800 CST          245      Standard   FDEE77829735C291DCC96233571F0E7C      oss://dyn-part-test/output/sid=101/dt=20120103/000000_0</pre>
ローカルにダウンロードして、中身を確認すると ORC という文字列が見えるため、確かにORCファイルです。
<pre class="EnlighterJSRAW" data-enlighter-language="null"># ossutil cp oss://dyn-part-test/output/sid=101/dt=20120103/000000_0 .
Succeed: Total num: 1, size: 245. OK num: 1(download 1 objects).
0.071985(s) elapsed
# cat output/sid\=101/dt\=20120103/000000_0 
ORC
P+
P+
xxxPNNxH?b?``???ь`?	L3?l`?I?M?????@&lt;?R?b?`
                                              `???`b??????**$*??`(???`???QP?`T???bbd?b.?L?b*?Ub?`aF+?+F!6				8?B?B?"??aK??"
                                                                                                                                                      (!0??ORC</pre>

<ul style="list-style-type: square;">
 	<li><span style="font-size: 14pt;"><strong>パーティションの追加</strong></span></li>
</ul>
動的パーティションを作成した後、継続してパーティションを追加することもできます。

```sql
ALTER TABLE output_tab ADD PARTITION (sid='102', dt='20120101') location 'oss://dyn-part-test/output/sid=102/dt=20120101/' PARTITION (sid='103', dt='20120102') location 'oss://dyn-part-test/output/sid=103/dt=20120102/';
```
パーティションを確認したら、追加したパーティションを見えます。
<pre class="EnlighterJSRAW" data-enlighter-language="shell">hive&gt; show partitions output_tab;
OK
sid=100/dt=20120101
sid=100/dt=20120102
sid=100/dt=20120103
sid=101/dt=20120101
sid=101/dt=20120102
sid=101/dt=20120103
sid=102/dt=20120101
sid=103/dt=20120102</pre>

<ul style="list-style-type: square;">
<li><span style="font-size: 14pt;"><strong>パーティションの削除</strong></span></li>
</ul>
不要なパーティションを削除することができます。テーブルのメタデータを確認するもできます。ただデータ置場のディレクトリおよびファイルは削除されずそのまま残ります。

```sql
ALTER TABLE output_tab DROP IF EXISTS PARTITION (sid='103',dt='20120102');
```

<pre class="EnlighterJSRAW" data-enlighter-language="shell">hive&gt; show partitions output_tab;
OK
sid=100/dt=20120101
sid=100/dt=20120102
sid=100/dt=20120103
sid=101/dt=20120101
sid=101/dt=20120102
sid=101/dt=20120103
sid=102/dt=20120101</pre>

<ul style="list-style-type: square;">
<li><span style="font-size: 14pt;"><strong>最後</strong></span></li>
</ul>
いかがでしたでしょうか、データインポート及び圧縮が行われる場合に、動的パーティションは有力な手段ですので、皆さんにご参考いただければ幸いです。