FirehoseとAthenaでSQL文が使える環境を構築してみた

# やりたいこと
AWSでデータを蓄積して、SQLチックにデータの検索がしたい。
でもRDSとかお高そうだし色々準備めんどくさいしやりたくない。
ということで、Athenaを使って楽してSQLっぽく検索できるようにしたい。
折角だしデータ蓄積（S3へのデータ保存）もFirehose使って楽してやりたい。

# 主な登場人物
![Athena.jpeg](https://qiita-image-store.s3.amazonaws.com/0/148269/68c57fa0-eb4d-b00b-dd6a-1aed3fd07e6d.jpeg)
### Firehose
JSONデータをよしなにフォルダ分けしてS3に保存してくれる子
### S3
シンプルストレージサービス。もう名前が説明だよねこれ
### Athena
S3に入ってるJSONとかのデータをSQLチックに検索できる
### Lambda
みんな大好きなコンピューティングサービス

# Firehoseの作成
多分コンソールから作るのが一番早くて楽です。

Firehoseの名前を入力して
予めデータを蓄積するS3バケットを作ってから、コンソールでkinesisFirehoseを選び、以下の通りに作成します
![スクリーンショット 2018-09-10 14.43.14.png](https://qiita-image-store.s3.amazonaws.com/0/148269/d6c52501-b4e6-d41e-d7ca-aba7b5659eeb.png)

予め作ったS3バケットを選択して
![スクリーンショット 2018-09-10 14.47.07.png](https://qiita-image-store.s3.amazonaws.com/0/148269/55177729-782c-2fc7-bb99-3b62bbe7af4c.png)

IAMRoleを自動生成でよしなに作ってもらって
![スクリーンショット 2018-09-10 14.49.55.png](https://qiita-image-store.s3.amazonaws.com/0/148269/5c8a5ea4-a428-6ec3-0c57-915580152942.png)

最後にCreateを押して、終わり。
![スクリーンショット 2018-09-10 14.50.04.png](https://qiita-image-store.s3.amazonaws.com/0/148269/468e4c7c-539b-44cc-e4b1-f7b5d38fbd02.png)
これでFirehoseは作成されます。上記の入力項目は初期値のままで問題ありません。

# Firehoseにデータを渡す
今回はAWSCLIで送ってみた。もちろんSDKを使って各種言語からも可能。

```python:コンソール
aws firehose put-record --delivery-stream-name athena-test --record '{"Data":"{\"Name\":\"Tyranitar\",\"CP\":3670,\"No\":248,\"GetDay\":\"2018/09/11\"}\n"}'
```
ポイントとして**最後に必ず改行コードを入れること**。
これがないとAthenaで検索時に出てこないことがある。

![スクリーンショット 2018-09-11 13.33.00.png](https://qiita-image-store.s3.amazonaws.com/0/148269/50bb7b95-5a8e-132b-078e-f438c0609cee.png)
S3にはこんな感じに保存される。フォルダも年月日時で自動生成される。
ただしフォルダの年月日時は、**UTC時間に保存されてしまう**のでそこは注意が必要。
また、**S3にファイルができるまで５分くらいかかる**。
どうも短期間で複数Firehoseからデータを送信すると１つのファイルにしようとするらしく、
その兼ね合いですぐには出力せずちょっと待ってから出力するっぽい。

# Athenaの作成
こっちもコンソールから作成するのが簡単だと思います

Athenaを開いてテーブルの作成→手動を選んで
![スクリーンショット 2018-09-11 14.20.35.png](https://qiita-image-store.s3.amazonaws.com/0/148269/8117bc4f-08ae-0328-adf5-0e5a19a103b6.png)

データベースを新しく作成、テーブル名とかは適当に
入力データセットの場所はFirehoseのS3の保存先を指定する
![スクリーンショット 2018-09-11 14.25.29.png](https://qiita-image-store.s3.amazonaws.com/0/148269/97028685-c253-27a4-6399-4f93e210b690.png)

形式はJSONを選択
![スクリーンショット 2018-09-11 14.27.58.png](https://qiita-image-store.s3.amazonaws.com/0/148269/2818c900-4b0a-ed96-ab01-43538e9714c8.png)

列の設定はFirehoseで送ったJSONの内容を設定
![スクリーンショット 2018-09-11 14.29.16.png](https://qiita-image-store.s3.amazonaws.com/0/148269/6e09176b-06cf-1ba6-0093-fa09afdf36bb.png)

最後にパーテションを列名dtにして作成
![スクリーンショット 2018-09-11 14.30.41.png](https://qiita-image-store.s3.amazonaws.com/0/148269/4f04fa14-e6be-0fdb-6e05-6d26c6ee56ab.png)

あとはテーブル作成を押せばAthenaのDBとテーブルが作成されます。
では早速SQLライクに表示...しようとするとなぜか０件。なんでや！
![スクリーンショット 2018-09-11 14.32.20.png](https://qiita-image-store.s3.amazonaws.com/0/148269/e6512e31-03f0-9139-2e01-549f4af62309.png)

原因はパーテションを設定していないからです。
ということでパーテションを設定してみましょう。

# Athenaのパーテション設定
そもそもAthenaのパーテションって何やねんって話ですが、
簡単に言えばS3のフォルダごとにパーテションとして値を設定することができます。
設定方法は下記のようなクエリを投げてあげれば設定できます。

```SQL:サンプル
ALTER TABLE <テーブル名> ADD PARTITION (<パーテションの名前>='<設定値>') location 's3://<S3バケット名>/<フォルダ名>';
```

実際にコンソールからやってみると、こんな感じになります。
問題なければ「クエリが成功しました」と表示されます。
![スクリーンショット 2018-09-11 14.38.50.png](https://qiita-image-store.s3.amazonaws.com/0/148269/23963381-62ca-0243-300b-ce2e48cdd50a.png)

他のフォルダも同様にパーテションを設定した後に再度検索してみると
今度はちゃんと検索結果が表示されました。
![スクリーンショット 2018-09-11 14.44.34.png](https://qiita-image-store.s3.amazonaws.com/0/148269/12542b04-0039-e970-b2ec-9dc17d1b904b.png)

当然、SQLチックに検索もできます。
![スクリーンショット 2018-09-11 14.46.20.png](https://qiita-image-store.s3.amazonaws.com/0/148269/e3733070-c594-1093-4bf0-a96295d08f6e.png)

また、パーテション（画面だとdt）でも検索することができます。
![スクリーンショット 2018-09-11 15.05.41.png](https://qiita-image-store.s3.amazonaws.com/0/148269/d5de414a-04a0-917c-f7cd-4f927b1567d3.png)

なんでパーテションを設定しないといけないかと言えば、
**パーテションで検索するとAthenaの費用と検索時間を小さくすることができます。**
というのもAthenaの内部の動きとしては
**該当バケットを全件取得→SQLにあわせて内容をフィルタリング**
となっており、バケットのデータが増えれば増えるほど費用も時間もかかってしまいます。
ですが、パーテションで検索することにより
**該当バケットの該当パーテションの内容のみ取得→SQLにあわせて内容をフィルタリング**
となるため、効率的に検索することができるようになります。

# パーテションの自動化
パーテションはフォルダごとに毎回SQLで設定してやる必要があります。めんどくさいですね。
一応自動設定となるフォルダ分けとかもありますが、なんとFirehoseから作成した場合は無理。
サービス連携ちゃんとやってくれよ！

仕方ないのでLambdaで自動生成するようにしましょう。下記はサンプル。なんとなくnode.jsで書きました。
OutputLocationはAthenaの検索結果を保存するバケットを設定します。
コンソールからAthenaを検索した場合、すでに自動で生成されているはずです。
テーブル名やS3バケット名とかはよしなに変更としてください。

```javascript:Lambdaサンプル
const AWS = require('aws-sdk');
const athena = new AWS.Athena({apiVersion: '2017-05-18'});
const moment = require('moment')

exports.handler = (event, context, callback) => {
    const today = moment().format('YYYY/MM/DD');
    const params = {
        QueryString: "ALTER TABLE pokelist ADD PARTITION (dt='" + today + "') location 's3://athena-pokelist/" + today + "'",
        ResultConfiguration: {
            OutputLocation: "s3://aws-athena-query-results-795622185554-ap-southeast-2/"
          }
        };
    athena.startQueryExecution(params, function(err, data) {
        console.log(data);
    });
};
```

上記Lambdaを１日１回適当なタイミングで動かして、パーテション毎日生成するようにしましょう。
時間ごとにパーテション分けしたい場合は、１時間に１回となります。
ただしここでの日付は例のごとく**UTCであること**に注意しましょう。

# FirehoseやAthenaの良い点まとめ
・ Firehoseがフォルダを日時で自動生成した上でJSONファイルを作成してくれる
・ RDSの構築とかなくAthenaで簡単にSQLライクなことができる
・ パーテションを設定すればAthenaの検索を高速化できる

# FirehoseやAthenaの問題点まとめ
・ Firehoseがデータ蓄積するのにタイムラグ（５分程度）がある
・ Firehoseのフォルダ自動作成が問答無用でUTC
・ Firehoseのフォルダ構成だとAthenaのパーテションを手動設定する必要がある

問題点がFirehoseばかりだしそれならS3に直接JSON保存すればいいじゃないかと言われるとその通りだが、
それはそれで実装がめんどくさいのも事実（パーテションしやすいようにフォルダ作って保存とかしないといけない）。
この辺りはなんとも言えないところ。

# 全体まとめ
RDS使わずにSQLっぽいことができるのはすごい素敵。
ただしそれなりに問題点もあるので適材適所で使い分けたほうがよさそう。
例えば、リアルタイムにデータ蓄積や検索をしたい場合は素直にdynamoDB使ったほうが実装的にもタイムラグ的にも良さげ。
後FirehoseがUTC限定なこと。これのせいで使い勝手がすこぶるよろしくない。