最適なデータベースを選択するために考慮するべきこと

こんにちは！少し前までProgateでエンジニアのインターンをさせていただいておりました、chisatonと申します。
Progateのアドベントカレンダーに参加させてもらえるだなんて思ってもみなかったので、とても嬉しい限りです。

今私はエンジニアとして別の会社で働かせていただいているのですが、今の部署とProgateではAWSを使用しているという点で共通点があるため、今回はAWSのサービス紹介も兼ねて、どんな時にどのデータベースを選択すべきなのか、についてまとめてみようかと思います。
元ネタは、最近参加させていただいたラスベガスでのAWS re:Inventの"Which Database to Use When?"というセッションになります。
[こちら](https://www.youtube.com/watch?v=KWOSGVtHWqA)に動画が既にアップされていますので、興味がある方はこちらを見てみてください。


# DB選択においてまず考慮すべき3つの項目
- Shape
- Size
- Compute 

## Shape

### Row Store

行指向データベースで使用されているストア方式です。MySQLなどでよく使われている有名なやつです。
例えばとある会社員の基本情報をまとめる時に１人ずつレコード単位でデータをまとめます。

![スクリーンショット 2017-12-09 17.28.35.png](https://qiita-image-store.s3.amazonaws.com/0/146353/5cbb15ff-42ce-a97e-bc8e-c967020cf77e.png)

例えば上図のようなデータの場合、赤枠で囲われている（行ごとの）データを一つのデータとして保存します。
その為、新しい社員が入ってきた場合は、一行データを追加するだけで済みますが、逆にここから社内の平均年齢を出したい時は一度全てのデータをとってきた上で年齢の項目だけに着目し、平均値を計算しなければなりません。

### Column Store


列指向データベースで使用されているストア方式です。後述するAWSのRedshiftなどではこの方式が採用されています。
上記の会社員の基本情報を再び例としますと、項目ごと（列ごと）にデータを保存します。

![スクリーンショット 2017-12-09 17.28.35.png](https://qiita-image-store.s3.amazonaws.com/0/146353/f5e4f205-a1b8-58b8-ad76-db48a8bb24bb.png)

この場合、新しく社員が入ってきた場合に、各項目ごとに保存しなければならないため、今回は「社員ID」、「名前」、「年齢」に対して書き込みが発生します。Row Storeと比べると書き込みの量が増えますが、一方で社員の平均年齢を計算をしたい時は年齢の項目だけを取り出して計算すればいいので、複雑な処理をせずに済みます。
また、列指向の場合、連続する同じデータを圧縮して管理出来るというのもメリットでしょう。

### Key-Value Store

ユニークな一つのキーに対して、一つの値を紐付けて保存する方式です。
例えば社員のIDに対して社員の名前を一対一で対応付けるとすると、下図のようになります。

```
1 : 佐藤
2 : 鈴木
3 : 髙橋
4 : 田中
5 : 伊藤
```

こうすることで、社員IDを使えば社員の名前を取得することができます。
Column StoreやRow Storeと比べるとデータ自体を複雑な構造のものにすることはできませんが、この場合社員IDでインデックスを張っているため、constantな時間で値を取得でき、非常に高速です。
インメモリキャッシュのredisなどではこの方式を利用しています。


### Document Store

よくNoSQLの代表格としてDocument Storeが紹介されることが多いです。いわゆるスキーマレスな（上記のRow Storeのように各レコードごとに各項目を用意していない）データベースです。

例えば、人の経歴についてのデータを保存するDBを考えてみましょう。
スキーマなDBに保存すると考えると、どんな項目が必要でしょうか？名前に年齢に最終学歴...他はどうでしょうか？
もしかしたら人によっては他の人にはないような項目の経歴があるかもしれません。実は留学していた。実は多言語喋れる。こんな時に役立つのがDocument Storeです。

```example.json
{"name": "佐藤", "age": 22, "edu-background": "Hoge University"},
{"name": "鈴木", "age": 24, "edu-background": "Fuga University", "foreign-lang": ["English", "Spanish"]},
{"name": "髙橋", "age": 25, "edu-background": "Fuga University", "foreign-lang": ["English"], "written-book": ["Intro to DB"]},
{"name": "田中", "age": 33, "edu-background": "Foo University",  "written-book": ["Intro to Java", "Advanced Java"]},
{"name": "伊藤", "age": 31, "edu-background": "Bar University",  "study-abroad": ["America", "Germany"], "foreign-lang": ["English", "German"]},
```

このようなDocument Storeの代表格としてはMongoDBやAWSのフルマネージドサービスDynamoDBなどが挙げられます。

### Graph Store

データ構造の勉強をしてれば必ず出てくるグラフ構造のストア方式です。
よくFBのような友人どうしのつながりなどのデータを保存する際に使われる事が多いです。
<img src="https://qiita-image-store.s3.amazonaws.com/0/146353/4af6e0cd-5517-6a7b-ec6c-0a7a3db07d38.png" width=70%>
例えば、アプリの機能として友人になった人の友人をおすすめの友人として紹介する場合、BさんがEさんと友達になると、BさんにはCさんFさんが「友達かも」と紹介されるような感じです。この時、既にAさんとは友達なので、紹介対象には入りません。
これを例えばスキーマなDBで友人の友人を探そうとすると、友人かどうかの中間テーブルを参照するなど、そこそこ複雑な処理が発生します。
もちろん、全体から特定のデータを取得したい時にはとあるノードから次のノードへとひとつひとつ探査をしていく必要があるため、全探査をしなくてはならないという短所もありますが、他のストア方式では届かなかった痒いところに手の届くストア方式であるかもしれません。



### Time-Series Store

時系列に保存、取得するストアです。
スキーマかスキーマレスかというよりかは、とある指定した時刻の間のデータが取れるようなstoreの事を指します。
例えばDynamoDBではrange keyなるものが存在し、保存されたtimestampを元に指定された時間の範囲内のデータを取ってきたりすることができます。
株価の動向を見る時などには、保存しておいた株のデータを１日単位や一週間単位で取得したりすることができます。

## Size

### Size at Limit

想定されるデータのサイズは有限のデータ(bounded)でしょうか、それとも無限に流れてくるデータ(unbounded)でしょうか。
社員のデータを保存するのでしたら、データの量はたかがしれています。10人かもしれませんし、大規模な会社でも2000万人ぐらいでしょう。これはboundedなデータです。
一方でユーザが訪れたサイトの情報を保存するとしたらどうでしょうか？1日一人あたり10サイト訪れる事を考えると、１日で日本だけでも、約1.2億 * 10 = 12億のデータを保存する必要があります。これはunboundedなデータに当てはまります。

### Working Set & Caching

実際に保存したデータはどの程度使用するでしょうか。
仮に１０年分のデータをDBに保存したとしても、もしかしたら使うデータは過去１年分のデータであり、９年分のデータは滅多に使用しないかもしれません。こんな時は1年分のデータのみキャッシュしてしまうというのも手かもしれません。

### Retrieval Size

取得したいデータは一つだけですか。それとも同時に複数のデータとってきたいでしょうか。
状況にもよりますが、取得したいデータが一つだけならkey-value Storeが圧倒的に構造が単純で高速でしょう。

### Partitionable & Monolithic

扱うデータは意味のある単位で区切ることができるでしょうか。
誰がどこにいるかというデータは、場所単位で分割できます。
一方で社員の給料明細は意味のある単位で区切るのがちょっと難しいデータかもしれません。
この違いによっては、データの保存を分割したりして高速化を図ることが出来るかもしれません。


## Compute

### Compute Functions

データを取り出す際の関数やクエリはどんなものが想定できるでしょうか。
データの保存の仕方によってはクエリや処理が簡単にも複雑にもなります。

### Throughput

どの程度DBに対してwriteとread数を要求するでしょうか。
readやwriteのスケールについても考える必要があります。

### Latency

どのくらいデータの取得に対して遅延を許容するでしょうか。
データの分析であれば多少遅くとも許容できるかもしれませんが、今アプリケーションを使ってくれているユーザーに対してデータの取得で時間をかけるわけにはいきません。

### Rate of Ingestion

どのくらいの割合でデータが挿入されるでしょうか。
bounded/unboundedでの前述例であれば、社員数のデータ挿入の割合はたかがしれてますが、訪れたサイトの情報のデータの挿入であれば一秒間に幾つものデータを漏らすことなくDBに保存しなければなりません。


# AWSのDB一覧

それでは上記の３項目を踏まえた上で、AWSのDBサービスを見ていきましょう。

- Amazon RDS
- Amazon DynamoDB & DynamoDB Accelerator(DAX)
- Amazon ElasticCache(Redis & Memcached)
- Amazon Neptune

## Amazon RDS

<img src="https://qiita-image-store.s3.amazonaws.com/0/146353/cc7ff73d-31c3-2c6c-a1fd-297aab4d149e.png" width=20%>

リレーショナル・データベースです。様々なDBエンジンがあり、その選択も適切に選択する必要があります。
が、やはりAmazon Auroraがすごいですね！MySQLよりは少しお高いですが、それ以上に良い機能が満載です。

Auroraは作られるとまず３つのAZに2つずつレプリケーション（同じDBを複数の場所に複製）します。
そして、仮にマスタのDBが死んでしまってしても、どれか他のスレイブDBがフェイルオーバー（マスタに昇格）します。
そのため、readのスケールが向上するだけでなく、障害耐性に対しても強いDBと言えるでしょう。
また、Auroraのストレージは、10GB単位で勝手に増えるため、ストレージの容量を常に監視する必要もありません。

ちなみに、今回のKeynoteではAuroraのまさかのmulti master機能が発表されました。
筆者はこの発表を聞いた瞬間発狂したのですが、まさかwriteがスケールする日が来ようとは...!!

## Amazon DynamoDB & DynamoDB Accelerator(DAX)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/146353/64dfb27b-ff78-a300-de91-acb7fb3c986a.png)

Document Store方式のNoSQLデータベースです。

DynamoDBも作られるとまず３箇所のAZにレプリケーションされます。
ストレージに関しては、勝手にパーティショニング（データの分割）が行われるため、気にする必要はありません。
そして、RDSとの大きな違いとしてreadとwriteのスループットをこちらで指定することができます。
そのため、RDSでは実現できないような膨大な量のデータのreadやwriteに対しても対応できます。

そしてDynamoDB Accelerator(DAX)はこのDynamoDBのデータをインメモリキャッシュしてくれるサービスになります。
二年ほど前のre:Inventで発表されて以来、未だサービスとして使うことができない（2017年12月執筆時）のが残念ですが、後々はデータの取り出しが高速化し、またDynamoDBへのreadが減ることはとてもうれしいですね！

## Amazon ElasticCache(Redis & Memcached)

<img src="https://qiita-image-store.s3.amazonaws.com/0/146353/013931ff-c929-5186-a03d-71bac629c21c.png" width=25%>

AWSのマネージドインメモリキャッシュサービスになります。
インメモリなので、膨大な量のデータのキャッシュには向いてませんが、RedisにしてもMemcachedにしてもkey-value Storeなので非常に高速にデータを返すことができます。
その他にもRedisにはsorted setといった追加したデータの値に応じてソートしてくれる機能や指定したキーに対して、指定した地理情報（緯度経度）を保存する機能などがあります。
Memcachedに関しては、ノードを追加することが出来るため、readもwriteもスケールさせる事ができます。

## Amazon Neptune

Graph型のフルマネージドデータベースサービスです。こちらも今回のre:Inventにて新しく発表されました!

![スクリーンショット 2017-12-09 19.57.57.jpg](https://qiita-image-store.s3.amazonaws.com/0/146353/cf0f435c-8f0f-0650-aa64-8116c1d48396.jpeg)

スケールが可能で、膨大な量のデータに対してミリ秒のクエリを使う事ができます。

こちらも3つのAZに足して6つのレプリケーションがなされるため、データを失う心配がほぼありません。
(筆者もこのへんはあまり詳しくはありませんが) 公式サイトによると、
`Apache TinkerPop や W3C の RDF など一般的なグラフモデルと、TinkerPop Gremlin や RDF SPARQL など関連するクエリ言語をサポート`
しているらしいので、既にどこかでGraph型のDBを使用されている方でしたら、比較的容易に扱えるかもしれません。

## まとめ

以上、表にまとめると以下のようになります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/146353/28baf104-246f-c0d2-1282-6ace647c3dcf.png)



# データ分析のためのAWSのサービス一覧

データを保存したら、次はデータの分析が必要になるかもしれません。DBサービスと共に分析のためのサービスも見てみましょう。

- Amazon Athena
- Amazon Redshift & Redshift Spectrum
- Amazon Kinesis Analytics
- Amazon Elasticserach

## Amazon Athena
<img src="https://qiita-image-store.s3.amazonaws.com/0/146353/510e1d87-1bb2-814c-cfde-ef48b41adc70.png" width=30%>


S3にあるファイルに対してSQLのようなクエリを投げることが出来るサービスです。
テーブル結合みたいなこともできるとか。
私の部署ではログの分析のするためにRedshiftを用いているのですが、いちいちログデータをRedshiftの方へコピーする作業が必要なため、現在Athena（またはRedshift Spectrum)を使おうかと検討中です。
分析した結果のデータを再び3Sへファイルとして保存したりできます。
~~クエリのページが完全にBigQuery~~


## Amazon Redshift & Redshift Spectrum
<img src="https://qiita-image-store.s3.amazonaws.com/0/146353/f4b07dbe-6537-a21c-2280-9e1a4c68068d.png" width=25%>

フルマネージドなデータウェアハウスです。
このRedshiftは大量のデータをクラウド上に保持し、それに対してSQLライクなクエリでデータの解析、分析を行います。
イメージとしましてはAthenaの分析データがS3にファイルとして上がっているのに対して、Redshiftではクラウド上に分析しているデータを保存しているといった感じです。
また、Redshift Spectrumを使えば直接S3のファイルに対してクエリを投げる事ができます。こちらはAthenaとは違ってSpectrum専用のクラスタが複数立ち上がって処理を行ってくれます。

別記事ではありますが、Redshiftを既に使っている人向けのパフォーマンスチューニングの記事も書きましたので、よろしければこちらも参考にしてください。
[Amazon Redshiftでより高速な処理を実現する](https://developers.cyberagent.co.jp/blog/archives/12950/)

## Amazon Kinesis Analytics
<img src="https://qiita-image-store.s3.amazonaws.com/0/146353/cc8ce370-deef-8446-ef0b-5a28ce23c5b7.png" width=25%>


Kinesis Analyticsは取得したデータに対してすぐに分析や解析をしたい時に役立ちます。
unboundedな（無限に流れている）データに対して基本的に収集はストリーム処理をするかと思いますが、そのストリームデータに対してSQLで処理を行います。
他にもデータの変換を行ったり、またタイムウィンドウを設定することができるので、どのくらいの時間間隔でクエリを走らせるのかを自由に設定することができます。

## Amazon Elasticsearch
<img src="https://qiita-image-store.s3.amazonaws.com/0/146353/63d47df1-f3dc-5c5c-c9a6-ca711b5d9e13.png" width=25%>


Elasticsearchといえば全文検索のイメージが強いですね。
Elasticsearchはログデータをキャプチャし、それと同時にそのデータに対してうまくインデックスを張ります。
そのため、ログデータに対して非常に高速にクエリの結果を返すことができます。
全文検索に関しても、同じようにうまく独自のアルゴリズムでインデックスを張ることで、検索に対応しています。

# まとめ

以上、表にまとめると以下のようになります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/146353/d184203d-f8cc-0977-0baf-a95c272b4366.png)



# 全体まとめ

長くなってしまいましたが、以上、re:Inventで聞いてきたセッションについてまとめさせてもらいました。
普段どのDBを使うべきかを話すことってあまりないかと思うのですが、比較的サービスの根幹をなすものなので、上記知識はとても重要なものであると思います。
プレゼンターも言っていましたが、

Which Database to Use When?

ではなく

Which Database **s** to Use When?

であるべきである、と主張していました。

どんなサービスを提供するかにもよりますが、より良いサービスを届けるために、適材適所なDBや分析サービスを選択していくことがとても大切だなぁと感じた講義でした。