[AWS] AWS Athenaの自分まとめ

# AWS Athena

- Athenaとは何かを知る
- Athenaをどう使えばいいかを知る
- Athenaの制限を知る
- Athenaのベストプラクティスを知る

## Athenaとは何かを知る

### 概要

```
Amazon Athena はインタラクティブなクエリサービスで、
Amazon S3 内のデータを標準 SQL を使用して簡単に分析できます。
Athena はサーバーレスなので、インフラストラクチャの管理は不要です。
実行したクエリに対してのみ料金が発生します。
```

> [AWS公式参照](https://aws.amazon.com/jp/athena/?nc=sn&loc=0)

- サーバーレス
    - 自動拡張
    - 運用不要
- S3の全てのデータを使用可能
    - データ容量上限なし
- S3にデータを配置してそのデータに対して標準SQLを用いて分析できるサービス
    - `Presto` ベース
- S3にあるデータを指定してスキーマの定義をしてSQLを実行
- 大規模データの多くのケースで数秒で結果が返ってくる
- 分析用データを準備するための複雑なETLは不要
- クエリが並列実行される
- データ形式には様々な形式に対応
    - CSV, JSON, ORC, Avro, Parquet etc
- ODBCやJDBCでの接続が可能
- [AWS Glue](https://aws.amazon.com/jp/glue/)と統合されている

### [料金](https://aws.amazon.com/jp/athena/pricing/?nc=sn&loc=3)

- スキャンしたデータに対してのみ支払い
- 1TBに対して5USD
    - 仮に 100GB をスキャンした場合 : `0.49 USD` => `54円`
- スキャンするデータ量を減らすことでコストを削減できる

### 導入事例

- [ナビタイム](https://ascii.jp/elem/000/001/585/1585498/)
    - https://www.slideshare.net/NavitimeJapan/athenaathena

## Athenaをどう使えばいいかを知る

- [公式ドキュメント](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/what-is.html)

### ユースケース

- 常時必要でではないスポット的な分析が必要なケース
- 大容量データを数秒で分析できるソリューションが必要なケース

### 実行まで

- S3バケットへデータを登録する
- データーベースの作成をおこなう
- テーブルの作成を行う
- クエリを発行する
- 結果取得する

### アクセス

- AWSコンソール
- API
    - AWS SDK
- CLI

### ソースデータの使用　（S3）

- S3に保存されているデータをリアルタイムでクエリ
- パフォーマンス向上とコスト削減
    - 列指向形式の使用 : `Parquet`

### テーブルとS3

- バージョニングされている場合は「最新」のデータのみをクエリ
- S3に対するアクセス許可が必要
    - ロールやポリシー
    - バケットのアクセス許可
- S3で暗号化するデータはAthenaと同一リージョンに保存する必要がある
- `Location句` で指定した同じバケットで複数のストレージクラスに保存されたオブジェクトでデータをクエリできる
- リクエスタ支払いバケットはサポート対象外
- GLACIERも対象外
    - ライフサイクルポリシーでGLRACIERに移行されたオブジェクトも対象外
- テーブル削除するとメタデータのみが削除されデータはS3に残る

### [データのパーティション分割](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/partitions.html)

- データをパーティション分割することでクエリのスキャンデータ量を制限できる
- パフォーマンス向上とコスト削減を達成できる
- パーティションには `Hive` を使用
- 全てのキーでデータをパーティションできる
    - 一般的には時間に基づいてパーティション分割
    - 年 / 月 / 日
- `CREATE TABLE` で `PRTITIONED BY` を指定する

#### データがパーティション済みでS3に `Hive` 方式で保存されている場合

- `S3::bucket/year=2019/month=01/day=01` のようなHive方式のディレクトリ構成でS3に保存済み
- `CREATE TABLE` で `PARTITIONED BY (year string, month string, day string)` を指定
- テーブル作成後 `MSCK REPAIR TABLE [table]` を実行しパーティショニング

### [列指向ストレージ方式](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/columnar-storage.html)

- `Apache Parquet` や `ORC` はデータを高速に取得できるようにした列指向ストレージ形式

#### 特徴

- 列ごとの圧縮によりS3ストレージ領域を節約してクエリ処理時の I/O や容量を軽減できる
- Parquet および ORC での `述語プッシュダウン` により、Athena クエリで必要なブロックだけを取得できる
- Parquet および ORC でのデータの分割により、Athena がデータの読み取りを複数のリーダーに分割して、クエリ処理時の並列処理を向上させることができる

#### データ変換

- `CTAS` で変換を行う
- `EMR` で変換処理を行う

```
既存の raw データを他のストレージ形式から Parquet または ORC に変換するには、
Athena で CREATE TABLE AS SELECT (CTAS) クエリを実行し、
データストレージ形式として Parquet または ORC を指定するか、
または AWS Glue クローラを使用します。
```

### テーブルデータのクエリ

#### クエリ結果

- Athenaはクエリ結果をS3へ保存
- クエリ実行毎に以下ファイルを生成
  - CSV形式での結果ファイル : `*.csv`
  - Athenaメタデータファイル : `*.csv.metadata`
    - このファイルを削除するAthenaにとって重要な情報が失われる

#### 結果保存先指定

- 個々のクエリ
  - 個々のクエリ実行時に `OutputLocation API` を使用して指定
- ワークグループ内の全てのクエリ
  - ワークグループの全てのクエリの保存先を指定する
- コンソールでのクエリ
  - デフォルトで自動でバケットが作成され保存される
- Athenaは結果保存先を指定しないとエラーになる

#### クエリ履歴

- 最大45日保存されている
- コンソールやAPIで履歴一覧や結果取得が可能
- 45日以上保存したい場合は自前でデータストアへ保存する処理が必要

### [View](https://dev.classmethod.jp/cloud/aws/amazon-athena-support-create-view/)

- 論理的なテーブル
- ビューを定義するクエリは1つのクエリでビューを参照されるたびに実行される

#### ユースケース

- データのサブネットをクエリする
- 複数のテーブルを1つのクエリに統合する
- 既存の基本クエリの複雑さを解消する

> むずかし〜いクエリの結果をViewさせることでデータを使う人は簡単になる

- [参考](https://dev.classmethod.jp/cloud/aws/amazon-athena-support-create-view/)

### [CTAS](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/ctas.html)

- `CREATE TABLE AS SELECT (CTAS)` クエリは新しいテーブルを別のクエリのSELECT結果から作成する
- S3の指定された場所にCTASによってい作成されたデータファイルを配置する

#### ユースケース

- raw データセットのクエリを繰り返さずに1 回のステップでクエリの結果からテーブルを作成する
- クエリ結果を他のストレージ形式 `Parquet, OCR etc` に変換する
- 必要なデータのみが含まれている既存のテーブルのコピーを作成できる

#### 考慮と制約

- CTASはS3にクエリ結果をS3にデータファイルとして書き込むがビューではデータを書き込まない
- CTASの結果を保存する先のS3は空である必要がある
  - CTASはバケット先がからであるかを確認する
  - データが既に存在する場合は上書きをしない
- 保存先を指定しない場合は自動でバケットを作成する
- クエリ結果の保存形式はデフォルトで `Parquet` 
  - 他には `PARQUET, ORC, AVRO, JSON, TEXTFILE` が指定可能
- クエリ結果はデフォルトで `GZIP` で圧縮される
  - Parquet および ORC では、 `SNAPPY` を指定することもできる
- 1 つまたは複数の列により、CTAS クエリの結果データをパーティション化することができる
  - 最大 100 個のパーティションを作成できる
- CTAS クエリの結果とバケットデータを 1 つまたは複数の列に保存するようにバケットを設定することができまる
- クエリ結果の暗号化が可能
- データ型は元のまま

#### バケット化とパーティショニング

- CTASクエリ結果で設定できるパーティションの数は `100個` まで
- S3に保存しているデータ側がパーティションされているとCTASの性能が向上する
- CTASクエリのバケット化はカーディナリティが高く値が均等に分散されている列によってデータをバケット化するときに上手くいく
- 大量のデータをスキャンしないようなバケット化およびパーティショニングが必要

#### クエリ例

##### バケット化とパーティショニングがある

- [CTAS TABLE AS 構文](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/create-table-as.html#ctas-table-properties)

```sql
CREATE TABLE ctas_parquet_bucketed 
WITH (
      format = 'Parquet',
      parquet_compression = 'SNAPPY', 
      external_location = 's3://my_athena_results/', 
      partitioned_by = ARRAY['nationkey'], 
      bucketed_by = ARRAY['mktsegment'], 
      bucket_count = 3) 
AS SELECT key1, name1, address1, phone1, acctbal, mktsegment, comment1, nationkey 
FROM table1;
```

### [配列](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/querying-arrays.html)

-  Athena では、配列の作成、連結、異なるデータ型への変換ができます。さらに、フィルタ処理、フラット化、ソートができる

### [スキーマの更新](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/handling-schema-updates-chapter.html)

- Athenaはスキーマの読み取りクエリエンジン
  - Athenaでデータを作成するときにデータの読み取りにスキーマを適用する
- データ形式によりスキーマの更新で出来ること出来ないことが存在する
  - [一覧表参照](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/handling-schema-updates-chapter.html#summary-of-updates)

### [ワークグループ](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/manage-queries-control-costs-with-workgroups.html)

- ワークグループを使用してユーザ・チームなどにワークロードを分離することができる
- クエリまたはワークグループ全体で処理できるデータ量に制限を設けコストを追跡できる
- アクセス制御もできる
- CloudWatchでのメトリクスも

### クエリログ

- CloudTrailで実行のログを追跡可能

### 圧縮形式

- Athenaは複数のデータ圧縮形式をサポート
- SNAPPY
  - `Parquet` のデフォルト圧縮形式
- ZLIB
  - `ORC` のデフォルト圧縮形式
- LZO
- GZIP


#### CSV, TSV, JSON

- これらのデータ形式はAthenaがファイル拡張子から圧縮タイプを判断する
- データが圧縮されている場合はファイル名に圧縮拡張子（gzなど）が含まれていることを確認する
  
#### Kinesis Data Firehose

- AthenaはFirehoseログに対するクエリではGZIP圧縮を使用する
- Kinesis Data FirehoseとAthenaではサポートされているSNAPPYのバージョンが異なるため互換性のある形式はGZIPのみ

### [DDL / SQL](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/ddl-sql-reference.html)

- Athena はDDLとANSI SQL関数および演算子をサポートする


## [Athenaの制限を知る](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/service-limits.html)

### DDL

- `20回 / 秒`

### SQLクエリ

- タイムアウト : `30分`
- クエリ文字列長 : `256KB (262144Byte)`
  - `UTF-8` でエンコードされる
- クエリ発行/停止 : `20回 / 秒`
  - `StartQueryExecution`
  - `StopQueryExecution`
- クエリ実行確認 : `100回 / 秒`
  - `GetQueryExecution`
- クエリ結果取得 : `100回 / 秒`
  - `GetQueryResults`

### ワークグループ

- 最大数 : `1000個 / リージョン`
 
## Athenaのベストプラクティスを知る

- [Amazon Athena のパフォーマンスチューニング Tips トップ 10](https://aws.amazon.com/jp/blogs/news/top-10-performance-tuning-tips-for-amazon-athena/)

### データをパーティションに分ける

## CTASについてもっと詳しく知る

- クエリした結果が保存されそのデータに対するテーブルが作成される

- 元となるクエリを作成する
- そのクエリに対するCTASを作成する

### トラブルシューティング

- `external_location` で指定するパスにはフォルダまで指定する必要がある
  - ex) `external_location='s3://output-ctas/tables/'`

## バケッティングについてもっと知る

- バケッティングは指定したカラムでデータを集約してデータファイルを作成すること
- したばってカーディナリティが高いカラムを指定することでよりインデックス化された状態を作ることができてスキャンするデータ量を減らすことができる
- `CREATE TABLE` で作成するときにバケッティングされたファイル数を指定する必要がある
  - 同一ファイル数で作成されていたらいいけど、、、

- [参考になる](https://dev.classmethod.jp/cloud/aws/amazon-athena-partitioning-vs-bucketing/)

## トラブルシューティング

### コンソール

- コンソールでテーブル作成やCTASで新規作成バケット指定してるのに「バケットが無い」とか「指定バケットすでに存在する」とかエラー出た時は、右上 `Setting` からデフォルトバケットを変更すると上手くいくかも