BigQuery vs Athena vs RedShift vs Hive


# はじめに
最近業務でAWS， GCPを選定する機会があったので，クエリエンジン周りの検証内容をざっとまとめます．
ちなみに筆者はクラウドにわか勢なので，間違っている点などあるかもしれません．詳しい方は笑って受け流してコメント欄等でご指摘いただけると幸いです．
ちなみに最終的にはGCS + BigQueryに決めたので，これらを若干贔屓目に書いているかも...

# 検証日時
2017/07/25なのでちょっと変わっているところもあるかも．
※ BigQuery周りのみ2018/5/24時点で少し更新をしました．

# 比較対象
- BigQuery (GCP)
- Athena (AWS)
- RedShift (AWS)
- Hive (オンプレOpenStack)

# 比較観点
- オブジェクトストレージの料金
- 料金
- 入出力に使えるデータのフォーマット
- Schemaの検出
- パフォーマンス

ただし，全項目に対して比較対象4つ全てを比較したわけではありません．その辺りも筆者のさじ加減で決めてます．


# 想定データ
ちなみに今回想定するデータの概要は以下の通りです．
(手っ取り早く済ませたかったのでそこまで大きなデータではやってません)

|      |        |
|:--:| :--: |
| フォーマット | CSV (各ファイルヘッダ行付き) |
| ファイル数 | 320 |
| ファイルサイズ | 約140MB |
| 合計ファイルサイズ | 約45GB |

# オブジェクトストレージの料金 
お金は大事ですね． まずはストレージの料金から比べます．
料金は各種操作にかかりますが，特にドミナントなのはストレージ代と転送量だと思うのでこれ以外はめんどくさいのでスルー．

料金系は変動が激しそうなので，まずリンクを貼ります． 

- [S3](https://aws.amazon.com/jp/s3/pricing)
- [GCS](https://cloud.google.com/storage/pricing)

とりあえず検証時点では，こちらの想定するデータ量で以下の通りでした． (東京リージョン)

- S3
    - Storage料金: $0.025 / GB
    - 転送料金: $0.14 / GB
- GCS
    - Storage料金: $0.023 / GB
    - 転送料金: $0.14 / GB

よっぽどデータが大きくない限り大した差にはならないので，決め手にはならないでしょう．
後日談の方でも少し述べますが，ストレージの機能としては若干S3の方が優れている気がしたので，若干高いのもまぁ妥当だと思います．


# 料金 
こちらもまずはリンクを.

- [Athena](https://aws.amazon.com/jp/athena/pricing)
- [RedShift](https://cloud.google.com/storage/pricing)
- [BigQuery](https://cloud.google.com/bigquery/pricing)

大きく違うのはAthena, BigQueryはサーバレスでクエリ課金，RedShiftはクラスタを常時立ち上げておくタイプで時間課金という点です．検証した時点での料金は以下の通りでした．

- Athena， BigQuery: \$5 / TB (スキャン量)
- RedShift: dc2.large 1台辺り `$0.314 / hour`

最初に記載したデータ量を1ヶ月毎日受け取ると想定して試算すると，
0.045 TB × 30日 × \$5 = \$6.75

一方RedShiftは上記1台のクラスタでも
\$0.314 × 24時間 × 30日 = \$226.08

とかなりの差がつきます．

RedShiftはBIツールのバックエンドなど，かなり頻繁にクエリをかけるワークロードと併用するというような条件下でないと優位性は出にくいのではないかと思いました． 機械学習のデータの前処理や，アドホックにクエリをかけたりというような用途で使う程度ならおそらくAthena, BigQueryの方が安くつくことが多いのではないでしょうか． 


# 入出力に使えるデータのフォーマット 
用途によりますが，データパイプラインの一部として利用する場合は，フォーマット次第でデータサイズの効率，処理の効率もかなり変わってくると思います．ということで入出力に使えるデータのフォーマットも比較しておきます．

## Athena

### [入力](http://docs.aws.amazon.com/athena/latest/ug/what-is.html) (残念ながら調査時点であったリンクが無くなっていた...) 
厳密にはもう少しありますが，ファイルフォーマットという意味では下記の4つです．

- JSON
- CSV/TSV
- Parquet
- ORC  

ざっくり説明するとAthenaはS3上のファイルに対しExternal Table(データの実体がクエリエンジンの外側にあるテーブル)としてクエリをかけられるPrestoです．テーブルにロードすることなく使えますが，クエリはS3のファイル集合を直接見に行くことになるので，最初からクエリに効率のよいフォーマット(上記でいうとParquetとORC)にして配置するのがベターです．これらのフォーマットはデータの圧縮効率もよく，ストレージコストも下げられますし，カラムナーの性質からSELECTでカラムを選択的にREADする際のスキャン量も減るので，クエリのコストも下げられます．

### [出力](http://docs.aws.amazon.com/redshift/latest/dg/copy-parameters-data-format.html)

- CSV

## RedShift
RedShiftは料金の時点で選択肢から消え気味だったのであまり細かく見ていないですが使えるフォーマットは下記の通りです．

### [入力](http://docs.aws.amazon.com/redshift/latest/dg/copy-parameters-data-format.html)
- CSV/TSV
- Avro
- JSON

### [出力](http://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD.html)
- CSV/TSV

厳密にはもう少しいろいろできそうですが，実現可能なメジャーなファイルフォーマットという意味ではCSV/TSVのみです．

## BigQuery
### [入力](https://cloud.google.com/bigquery/data-formats)
- Avro
- JSON
- CSV
- Parquet
- ORC

BigQueryの方はAthenaと似ていますが，こちらはBigQuery上にデータをロードして使うこともできます．
入力としてカラムナーフォーマットのファイルは取れませんが，テーブルにロードする際に，効率のよいカラムナーフォーマットに変換しているようです．
(中の状態まで詳しく知りたい方は[こちらの論文](https://static.googleusercontent.com/media/research.google.com/ja//pubs/archive/36632.pdf)を参照．なかなか面白いです)

~~Athenaと同様にGCS上のデータをExternal Tableで参照してクエリをかける機能もあるにはあるのですが，US, EUリージョンのバケットでないとちゃんとクエリ結果を出力できないようでした．~~

BigQueryでもAthenaと同様にGCS上のデータをExternal Tableで参照してクエリをかけられます．
ただし，External TableにクエリをかけるにはBigQueryのDataSetのリージョンと，データが置かれているバケットのリージョンが一致している必要があります．今まではDataSetを`US`か`EU`にしか作れなかったのですが，新たに`asia-northeast-1`も追加され，東京リージョンのバケットに対してもExternal Tableとしてクエリがかけられるようになりました!
また，新たにParquetとORCのカラムナーファイルへの対応も追加されたので，これらのフォーマットでGCSに配置し，External Tableとして参照するのも選択肢の一つにはなったのではないでしょうか．

上記以外のフォーマットだと，External Tableでのクエリの効率はあまりよくないでので，手間を惜しまずちゃんとテーブルにロードして使った方がいいです．
料金が気になるかもしれませんが，ロード自体には料金はかかりませんし，ロードしたデータには生存期間が設定できます．
また，内部でカラムナーに変換してくれるのでクエリ時のスキャン量が削られるためクエリ料金の方は下げられます．

### [出力](https://cloud.google.com/bigquery/docs/exporting-data)
- Avro
- JSON
- CSV

[Apache Arrow](https://arrow.apache.org)も少し話題に上がってきているので，Parquet出力もあると嬉しかった．



## Hive
Hiveも案外いろいろ使えますね．

### [入力](https://cwiki.apache.org/confluence/display/Hive/FileFormats)
[こちら](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-RowFormats&SerDe)も合わせて参照

- Avro
- CSV/TSV
- JSON
- Parquet
- ORC

### [出力](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-StorageFormatsStorageFormatsRowFormat,StorageFormat,andSerDe)
- Avro
- CSV/TSV
- Parquet
- ORC

意外にも入出力フォーマットではHiveが最強です．
次点でBigQuery．カラムナーの対応がないのが惜しいです．
AWS系2つでは出力がCSV/TSVしかない点で，ちょっと弱めです．
とはいえCSV/TSVで問題になることも少ないので，ここもそこまで決め手にはならないかもしれません．


# Schemaの検出
アドホックにクエリをかけたいようなケースで自分でテーブルを手入力で作るのは結構大変です．
特にフィールド数が数百，数千とあったりすると正直心が折れます． なんらかのスキーマ検出機能を自作することになるかもしれません．
ということでスキーマ検出周りも少し調べました．

- Athena
  - 特になし，事前に自分でテーブル定義を行う必要がある．
- RedShift:
  - こちらも特になし，Athenaと同様事前に定義が必要
- BigQuery:
  - CSVはヘッダ行，Avroはファイルの先頭にスキーマを記述しておくと自動で解決してくれる． 
  - さらにAvroは後方互換がある組み合わせならスキーマが異なっても自動で解決してくれました．
- Hive:
  - 特になし，自分で定義する必要がある．

ここは完全にBigQuery1強でした．
特にAvroは標準ライブラリでファイルに書き出す分には自動でファイルの先頭にスキーマをつけてくれているので，そこまで先頭のスキーマの制約も気にならずに使えると思います．


# パフォーマンス 
やはり一番気になるのはパフォーマンスでしょう．最初に記述したデータを想定して以下のようなクエリで実行時間，スキャンされたデータ量を計測しました．

クエリ1. 条件に合うレコード数を数える
クエリ2. 条件に合うレコードの集合と，条件に合わないレコードの集合から適当にサンプリングした集合の和集合を返す

## Athena
クエリ1，クエリ2は具体的には次の通りです．


**クエリ1**

```sql
SELECT COUNT(*) FROM samples WHERE length(column1) != 0
```

**クエリ2**

```sql
 (SELECT * FROM samples WHERE length(column1) = 0 AND random() < 17000000.0 / 352824342.0)
 UNION ALL
 (SELECT * FROM samples WHERE length(column1) != 0)
```

**結果**

|クエリ| 実行時間 (sec) | スキャンしたデータサイズ (GB) |
| :---: | :---: | :---: |
| クエリ1 | 7 sec  | 42.17 GB |
| クエリ2 | 115 sec  | 84.44 GB  |

## RedShift
RedShiftは `dc1.large` 1台で計測していました．
実際はクラスタのサイズを大きくすればもっと速くなるはずですが，既にお値段にげんなりしていたので，1台のパフォーマンスしか見ていません．
ちなみにこの構成だとデータのロード自体に33分かかっていた...

**クエリ1**

```sql
SELECT COUNT(*) FROM samples WHERE length(column1) != 0
```

**クエリ2**

```sql
UNLOAD ('
  (SELECT * FROM samples WHERE length(column1) != 0) UNION ALL
  (SELECT * FROM samples WHERE length(column1) = 0 AND random() < 17000000 / 352824342 )
') TO 's3://sample-bucket/redshift.csv'
WITH CREDENTIALS 'aws_access_key_id=<access_key>;aws_secret_access_key=<secret_key>'
DELIMITER AS ','
PARALLEL ON;
```

**結果** (クエリ課金でないのでスキャン量は見てません)

|クエリ| 実行時間 (sec) |
| :---: | :---: | :---: |
| クエリ1 | 7 sec  | 
| クエリ2 | 355 sec  |

RedShift使うならかなりお金積まないとキビシイ...



## BigQuery
BigQueryはデータをロードしてからクエリをかけています．
ちなみにデータロードは1分程度でした．

**クエリ1**

```sql
SELECT COUNT(*) FROM samples WHERE length(column1) != 0
```

**クエリ2**

```sql
SELECT * FROM
    (SELECT * FROM `dataset.samples` WHERE column1 IS NULL AND  RAND()　< 17000000 / 352824342),
    (SELECT * FROM `dataset.samples` WHERE column1 IS NOT NULL)
```

**結果**

|クエリ| 実行時間 (sec) | スキャンしたデータサイズ (GB) |
| :---: | :---: | :---: |
| クエリ1 | 2 sec  | 0.0126 GB  |
| クエリ2 | 26 sec  | 53.1 GB |

AthenaはS3からExternalなクエリ， BigQueryはテーブルにロードした上でのクエリなので若干フェアではありませんが，ロード時間を合わせてもBigQueryが最速でした．特に一旦テーブルに読み出してから何回もクエリをかけるような場合だとパフォーマンスはBigQuery1強といえる結果でしょう．


# 総括

クエリエンジンに関しては，とりあえず自分の想定する用途では，ほぼBigQuery1強状態でした．
速いしスキーマも自動検出してくれてロードも楽だし，いろんな入出力フォーマットに対応しています．

オブジェクトストレージは料金的にはS3もGCSも同じくらいになるのですが，やっぱりGCPは全体的にACLが弱いです．
特に気になった点として，GCSでは最小でもバケットという粒度でしかACLを設定できず，S3のようにPrefixベースでのアクセス権限の設定ができません．
オブジェクトストレージではまだまだS3に分があると言えます．

個人的な見解ですが，ざっくりまとめると，データを厳格に管理することを重視するならS3 + Athena
ACLの弱さに目を瞑ることができるならGCS + BigQueryの採用をオススメします．


# 最後に
かなり雑な検証でしたが，オブジェクトストレージ， クエリエンジンの観点からAWS, GCPの比較をまとめました．これからクラウドの選定を行う方々の一助となれば嬉しいです．

また，個人の見解を述べている箇所も多々あるので，間違い等あればコメント欄でご指摘いただけると幸いです．