大量アクセスが想定されるデータベースの疎結合化

# 0.はじめに

今回はデータベースとの結合方法に関して、思いつく方法と実際にやってみた上でのメリット、デメリットを解説していきます。

# 1.単純結合（アプリケーションとの直接接続）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661133/239e0f6d-0072-7944-0698-24251caf264a.png)


一番汎用的な構成で、読み込み（スレイブ、リード）サーバー群が冗長化されており、書き込み（マスター、ライト）サーバーは単一化されている、シングルマスタ構成

### メリット

- **非常に構成しやすい**
マスタ×1、スレイブ×nの構成でレプリケーションを貼り、アプリケーション内でスレイブサーバーへの接続を冗長化させる事のみで構成可能で非常に構成しやすい
AWS環境の場合はRDSを利用すれば、スレイブ群に対して、単一のエンドポイントで内部的に負荷分散をしてもらえるため、感覚的に構成できる

### デメリット

- **書き込みの冗長化がされていない**
書き込みデータベースは単一の構成で、有事の際にオンプレミス環境の場合はマスタ昇格の作業が必要となり、ダウンタイムが生じる
またAWS環境でRDSにマスタを設定している場合にも、フェイルオーバーによるダウンタイムが発生する

- **大量の書き込みに対して弱い**
瞬間的に大量に書き込みが行われた場合に、接続数が上限を超える可能性が高い
その場合にアプリケーションの処理が失敗し、データの不整合、または処理の中断が行われる

# 2.単純結合（マルチマスタ構成）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661133/d8ed5ebe-12a7-0a31-a06d-7bfe55bc069a.png)

1の単純結合に対して、書き込みデータベースをマルチマスタ構成に変更して冗長化した構成

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/aurora-multi-master.html

### メリット
- **書き込みデータベースの冗長化**
有事の際でも書き込みデータベースが冗長化されているため、接続先を切り替える事が可能となる

- **負荷分散が可能**
スケーラビリティが向上し、書き込みデータベース負荷を軽減できる

### デメリット
- **同一のエントリに対しての整合性が取れていない**
同時に同じレコードへの更新が行われるサービスには向いていない
接続先データベースによって結果が異なるケースが想定される

- **大規模サービスでの実用例が少ない**
整合性が取れないデメリットが大きいため、サービスが大きければ大きいほどマルチマスタ構成には不向きである事が分かる

Amazon Aurora Multi-Masterが2019年にリリースされましたが、まだ本番サービスで使われている例は多くなく、加えて既存のデータベースの切替はできないため、新たにデータベースを作成する必要がある

# 3.DB書き込み用APIサーバーを用いた疎結合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661133/fa1f6b9d-7825-50a8-b38f-69eacbed5548.png)

データベースへの書き込みに対して、APIサーバーを経由する事で直接的にアプリケーションが書き込み用データベースに接続する事を防ぐ

マイクロサービス化で提唱される事が多い構成

### メリット
- **APIサーバー内でデータの整形や改変も可能で、応用性が高い**
POSTするデータに何か付加したり、状況に応じた改変を挟むことが可能になる
- **マスタへの接続が困難になった場合に、APIサーバー側でトライキャッチする事ができる**
接続できなった場合の再接続やクエリの保持等、APIサーバー側でまとめて処理する事ができるため、有事の際の復旧は恐らく早い

### デメリット
- **管理コスト、サーバーコストの向上**
- **APIサーバーへの接続ができなかった場合の対応**
APIにアクセスが集中し、そもそも接続が困難となった場合も、データベースへの接続ができない場合と同じ問題が生じる

# 4.サーバーレスサービスを中継した疎結合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661133/a9f911aa-c4e8-11fe-51a5-1d45d68a730f.png)

Lambdaを介してデータベースの接続を行うケース
Lambdaに環境変数としてデータベースへの接続情報を設定する事で、直接データベースに接続する事が可能(pyMySQL等）

https://aws.amazon.com/jp/premiumsupport/knowledge-center/connect-lambda-to-an-rds-instance/

### メリット
- **耐障害性が高い**
Lambda自体がマネージドサービスで、サーバーレスとなり、よほどの事が無い限りはアプリケーションからLambdaへの接続ができない可能性は低い(SLA99.9%)

### デメリット
- **セキュリティレベルが低い**
APIのパラメーター、ポスト値にクエリ、または書き込み予定のキーやバリューを乗せるため、プロトコルによってはかなりセキュリティが低い
- **データベース負荷**
データベースは依然として単一構成であり、スパイクで高負荷が発生した場合には応答できない可能性がある

## 5.4のLambdaをSQSに置き換えた構成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661133/4c4f9be6-5a14-56f1-a0ea-c308018d8b4c.png)

- ①アプリケーションからデータベースへの書き込み内容をjson形式等に変換して、SQSにキューとして送信する
- ②キューを処理してデータベースに書き込む用のサーバーを用意して、ポーリングで待機させ、キューを受け取り、データベースへの書き込みを行う

https://docs.aws.amazon.com/ja_jp/sdk-for-php/v3/developer-guide/sqs-examples-send-receive-messages.html

### メリット
- **データベースへの書き込みが可能な際のみ処理を行う**
FIFOキューを活用し、データベースへの書き込みが正常に行えた際のみ削除プロセスを実行する事で、万が一データベースが書き込み不能な状態になっていた場合でもキューが保持され、復旧時に書き込み再開が可能
また書き込みサーバーを水平スケールしても、同一のキューを受け取る可能性がない
- **耐障害性が高い**
Lambdaと同様に、サーバーレスのマネージドサービスであり、アプリケーションから接続できない事態に陥る可能性は限り無く低い(SLA99.9%)

### デメリット
- **非同期的な書き込みが発生する**
アプリケーション側で書き込み処理をSQSに飛ばした場合に、実行が完了した旨を受け取る方法がないため、その後すぐに書き込んだデータを取り出す等ができない
また待機しているキューの数によっては、書き込みに時間が掛かる可能性がある（書き込み用のサーバーを水平スケールする事で回避可能）
- **セキュリティレベルが低い**
Lambdaと同様にキューとしてクエリや書き込み予定のキー、バリューを載せるため、セキュリティレベルの低さは否めない

# まとめ

- 耐障害を高めるためには、2つの手段が取り得る
- **①サーバーそのものを冗長化する**
- **②中間サービスの耐障害性を高める**

オンプレミス環境では特に、既存の環境にサーバーレスサービスを組み込むハードルはとても高いが、LambdaやSQSは耐障害性はもちろん、料金コストにもコミットする場合が多い

またLambda Function URLの実装等、AWSサービス以外からでも呼び出す難易度が下がりつつあるため、単調な処理はサーバーレスを利用する事で安定した稼働に繋げる事ができる

SQSは、メッセージ配信等はもちろんだが、FIFOを用いた先入れ先出しであらゆる処理を一旦キューに保持する事で、データベース障害時に復旧が容易となる

是非参考にしてみてください

**【おすすめの記事】**

https://qiita.com/ryoya1122/items/502c07410197746e79e4

https://qiita.com/ryoya1122/items/f72486e8db0508a7ee08