DBのレプリケーションを理解する

## はじめに
データベースのレプリケーションを理解するためのメモです。

## 目次
- [レプリケーションとは](https://qiita.com/esnir74/items/0f6a57ae1406138fd373#%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%81%AF)
- [目的](https://qiita.com/esnir74/items/0f6a57ae1406138fd373#%E7%9B%AE%E7%9A%84)
    - [負荷分散](https://qiita.com/esnir74/items/0f6a57ae1406138fd373#1-%E8%B2%A0%E8%8D%B7%E5%88%86%E6%95%A3)
    - [可用性の向上](https://qiita.com/esnir74/items/0f6a57ae1406138fd373#2%E5%8F%AF%E7%94%A8%E6%80%A7%E3%81%AE%E5%90%91%E4%B8%8A)
    - [レイテンシの低下](https://qiita.com/esnir74/items/0f6a57ae1406138fd373#3-%E3%83%AC%E3%82%A4%E3%83%86%E3%83%B3%E3%82%B7%E3%81%AE%E4%BD%8E%E4%B8%8B)
- [レプリケーションの分類](https://qiita.com/esnir74/items/0f6a57ae1406138fd373#%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E5%88%86%E9%A1%9E)
    - [更新の観点からの分類](https://qiita.com/esnir74/items/0f6a57ae1406138fd373#1-%E6%9B%B4%E6%96%B0%E3%81%AE%E8%A6%B3%E7%82%B9%E3%81%8B%E3%82%89%E3%81%AE%E5%88%86%E9%A1%9E)
    - [データが複製されるタイミングによる分類](https://qiita.com/esnir74/items/0f6a57ae1406138fd373#2-%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8C%E8%A4%87%E8%A3%BD%E3%81%95%E3%82%8C%E3%82%8B%E3%82%BF%E3%82%A4%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%AB%E3%82%88%E3%82%8B%E5%88%86%E9%A1%9E)
    - [内部的な実現方法による分類](https://qiita.com/esnir74/items/0f6a57ae1406138fd373#3-%E5%86%85%E9%83%A8%E7%9A%84%E3%81%AA%E5%AE%9F%E7%8F%BE%E6%96%B9%E6%B3%95%E3%81%AB%E3%82%88%E3%82%8B%E5%88%86%E9%A1%9E)
- [参考](https://qiita.com/esnir74/items/0f6a57ae1406138fd373#%E5%8F%82%E8%80%83%E3%82%B5%E3%82%A4%E3%83%88) 
    
## レプリケーションとは
「複製」を作る機能のこと
あるデータベースのデータと全く同じものを別のシステムに作成すること

※そもそも書き込むテーブルを分けるのは「パーティショニング」というみたいです

## 目的
レプリケーションには以下のような目的があります。
1. 負荷分散
2. 可用性の向上
3. レイテンシの低下

それぞれ詳しくみていきましょう

### 1. 負荷分散
レプリケーションによって**スケールアウト**を実現できます。

負荷分散の手法として
- より処理能力の高いマシンの導入（スケールアップ）
- 複数のマシンを導入（スケールアウト）

がありますが、スケールアップをしようとしたとき、気になるのはマシンの価格です。
また、10倍の処理をしようとしたとき、10倍の性能が良いマシンを導入するのは現実的ではありません。
スケールアウトでは、処理を10台のマシンに分散させることによって、理論上10倍の処理を行うことができます。


### 2.可用性の向上
何らかの理由でデータベースサーバがダウンしたとしても、アクセス先を複製したデータベースに変更することで、引き続きデータベースを利用することができます。

### 3. レイテンシの低下
データベースサーバが物理的に分散されると、各ユーザーからの物理的な距離が近くなるので、レイテンシの低下が期待できます。

※レイテンシ：ユーザーがデータを送信してから応答があるまでの遅延時間

## レプリケーションの分類
レプリケーションは様々な観点から分類することができます。
1. 更新の観点からの分類
2. データが複製されるタイミングによる分類
3. 内部的な実現方法による分類

### 1. 更新の観点からの分類
データ更新の観点からレプリケーションを分類すると、
- シングルマスタレプリケーション
- マルチマスタレプリケーション
の２つがあります。

#### シングルマスタレプリケーション
更新可能なデータベース（マスタといいます）は１つ。
それ以外の、複製されたデータベース（スレーブといいます）は読み取り専用です。
更新の場合は必ずマスタを更新します。
読み取りの場合に、アクセスを各スレーブに振り分けることによって、負荷分散を実現しています。
このように、行いたい処理によってアクセスするデータベースが変わるので、データベースより上の層での処理が必要となります。

#### マルチマスタレプリケーション
複数、またはすべてのデータベースの更新が可能なレプリケーション。
どのデータベースにアクセスしてもいいので、データベースより上の層での処理は不要となります。
ただし、整合性の観点から、同時更新の処理が難しくなり、レプリケーションによる負荷分散の効果が小さくなります。
複数のデータベースでの整合性を保つために、データの更新に先立ち、全DBについて対象のデータのロックを取得するなどの方法がありますが、更新性能が大きく落ちたり、オフライン更新などによって同時更新を防げない場合があります。
Google Docsを複数人で更新しようとしたときに、誰かが更新できなくなるイメージです。


:::note warn
##### シングルマスタレプリケーションによるスレーブは更新不可能とは限らないので注意
スレーブが更新可能かどうかはRDBMSの種類に依存します。更新可能であったとき、スレーブが更新されたとしても他のデータベースに変更が反映されることはないので、スレーブに対する更新はご法度です。
MySQLでは習慣として、my.cnfファイルでread_onlyオプションをつけておくべきとされています。フェイルオーバーとの兼ね合いでつけない場合もあるみたいですが。
:::

### 2. データが複製されるタイミングによる分類
データの複製タイミングという観点からレプリケーションを分類すると、
- 同期レプリケーション
- 非同期レプリケーション
の２つがあります。

#### 同期レプリケーション
スレーブにデータが反映されるのをマスタで待ちます。
スレーブとマスタの内容が同一であることが保障されます。
ただし、更新性能がややダウンするという懸念もあります。

#### 非同期レプリケーション
スレーブへのデータの更新が非同期で行われます。
マスタが更新された直後に、スレーブへの問い合わせがあった場合、マスタのデータとは異なるデータが取得される可能性があります。
更新性能は同期レプリケーションよりも良いとされています。


### 3. 内部的な実現方法による分類
内部的な複製の実現方法という観点からレプリケーションを分類すると、
- データを転送する方法
- SQLを転送する方法
の２つがあります。

#### データを転送する方法
更新されたデータをそのまま転送する方法です。
確実に同じものが転送されます。
行データを転送するので、データ量が大きくなります。

#### SQLを転送する方法
Proven technology（歴史的に確立された手法）です。
SQLが転送されるので、データ郎は少なくなります。
都度データが生成されるものなど、一部のSQL文だと転送元と転送先でデータが異なる可能性があります。

MySQLでは`mixed-format logging`と呼ばれる、上記２つの方法を組み合わせた方法も存在します。
デフォルトではSQLを転送し、特定のステートメントや使用されているストレージエンジンに応じてデータを転送する方法に変更するという手法です。
参考: https://dev.mysql.com/doc/mysql-replication-excerpt/8.0/en/replication-formats.html

## レプリケーション遅延
レプリケーションの遅延対策について追記。
~~面接で詰められたので~~

スレーブへのデータの反映は高速に行われるので、スレーブへの更新の遅延はそれほど気になることは多くないが、以下のような原因で、発生する場合がある。

1. ネットワークの遅延
2. 巨大なトランザクションの実行
2. スレーブの負荷が高すぎる


### 1. ネットワークの遅延

単位時間当たりの香辛料よりもネットワーク帯域の方が上回り、データの転送が滞りなく行われていれば、ネットワークに起因する遅延は生じない。
- 遠隔地へのレプリケーションで、回線が細い場合
- ネットワークの障害

#### 対応策
ネットワーク環境の見直し

### 2. 巨大なトランザクションの実行
もっともポピュラーな遅延の原因
- ステートメントベースのレプリケーションを実行していた場合、スレーブ上でも全く同じSQLが実行されることになるので、マスタの更新に１時間かかった場合は、スレーブの更新にも１時間程度かかると考えられる
#### 対応策
巨大なトランザクションは実行しない。いかんともしがたい場合はメンテナンスウィンドウを設けて実施する

### 3. スレーブの負荷が高すぎる
スレーブが処理できる量を超過した更新がマスタからやってくる。どのリソースがボトルネックになるかは状況次第だが、概ね次の３パターンに分けて考えると良い。

- CPUバウンド
    - スレーブへの参照負荷が高すぎて、スレーブSQLスレッドに割り当てるCPUリソースが枯渇している状態
    - CPUをスケールアップしよう
- Memoryバウンド
    - InnoDBのバッファプールがワーキングセットに比べて小さくて、頻繁にディスクへのアクセスが生じている状態
    - InnoDBバッファプールサイズを増やそう
- I/Oバウンド
    - マスタでは更新が並列で行われ、グループcommitによってログへの変更がまとめて行われるが、スレーブではトランザクションは一度に１つずつしか実行できないため、マスタよりもディスクへの同期回数が多くなる状態
    - スレーブのInnoDBに、`innodb_flush_log_at_trx_commit=2`を設定して、コミットごとにストレージにデータを書き込まないようにする
    - １秒ごとのイベントで初めてストレージへの永続的な保存を行う


## 参考サイト
https://oss-db.jp/dojo/dojo_10

https://dev.mysql.com/doc/mysql-replication-excerpt/8.0/en/replication-sbr-rbr.html

https://www.youtube.com/watch?v=scp8QnjqKsgc,

https://nippondanji.blogspot.com/2011/12/mysql.html

https://docs.netapp.com/ja-jp/ontap-apps-dbs/mysql/mysql-innodb_flush_log_at_trx_commit.html