レプリケーション

レプリケーション

2025-01-03

データ指向アプリケーションデザインの5章、レプリケーションのメモ書きをする記事です。正確性等は確認できていません。

目次

  • レプリケーションの基礎
    • レプリケーションの種類
    • レプリケーションの設定オプション
    • レプリケーションする理由
  • リーダーベースレプリケーション
    • レプリケーションの同期・非同期
      • 同期のデメリット
      • 非同期のデメリット
    • フェイルオーバー
      • フェイルオーバーの問題点
    • レプリケーションログ
      • ステートメントベースのレプリケーション
      • 論理ログレプリケーション
      • トリガベースレプリケーション
    • レプリケーションラグ
  • めも

レプリケーションの基礎

レプリケーションはデータを複数の場所に複製することで、可用性やスケーラビリティを高める。

レプリケーションの種類

  • シングルリーダー
  • マルチリーダー
  • リーダーレス

レプリケーションの設定オプション

  • 同期 or 非同期
  • 障害を起こしたレプリカの扱い

レプリケーションする理由

  • 障害への耐性(1つが動かなくても他でカバーできる)
  • スケーラビリティ(いっぱいリクエスト処理できる)
  • レイテンシ(地理的にユーザーに近い場所で処理できる)

リーダーベースレプリケーション

「すべてのデータがすべてのレプリカに行き渡っていることを保証するにはどうしたら良い?」という疑問があり、それの対策として最も一般的な解決策がリーダーベースレプリケーション。

リーダーベースレプリケーションでは一つのリーダーレプリカとフォロワーレプリカに分けられる。読み取りはリーダー・フォロワー関係なくできるが、書き込みができるのはリーダーのみ。

この時のリーダーはreaderではなくleaderなので、リーダーではなくプライマリやマスターと言われたりもする。

以下のような動作となる

  1. リーダーレプリカに対して書き込みのリクエストをクライアントが送る
  2. リーダーはローカルに新しいデータを書き込む
  3. リーダーは変更データをレプリケーションログor変更ストリームの一部としてすべてのフォロワーに送信する
  4. フォロワーはリーダーから受け取った変更データに沿って自身のローカルストレージを更新する

レプリケーションの同期・非同期

レプリケーションには同期と非同期があり、同期の場合はクライアントがリーダーに書き込みリクエストを送信した後、リーダーが書き込みを行い、フォロワーもすべて更新された後に成功のレスポンスをクライアントに返却する。

それに対して非同期の場合はクライアントがリーダーに書き込みリクエストを送信した後、リーダーが書き込みを行いすべてのフォロワーが更新されるのを待たずに成功のレスポンスをクライアントに返却する。

また準同期型というのもあり、これは一つのフォロワーを同期型とし、それ以外のフォロワーを非同期型にするというもの。これによって1つのリーダーと1つの同期型フォロワーの2つのノードに最新データが存在していることが保証される。

同期のデメリット

同期型のデメリットはすべてのフォロワーのうち、どれか一つでも障害(ネットワークやフォロワーのクラッシュ)が発生した場合にシステム全体が使えなくなってしまうこと。なので全てを同期型にするのは現実的ではない。

非同期のデメリット

リーダーに障害が発生し、リカバリできない時、フォロワーに書き込んでいないデータが失われてしまうこと。

フェイルオーバー

リーダーノードに障害が発生した場合は別のフォロワーをリーダーに昇格させ、クライアントからは新しいリーダーに対して書き込みのリクエストを行うよう変更しなければならない。また他のフォロワーもデータ変更を新しいリーダーから受信する必要がある。これらの変更プロセスをフェイルオーバーという。

フェイルオーバーは次のようなプロセスとなる

  1. リーダーの障害を検知する
    • ノード群は頻繁にメッセージをやり取りしており、しばらくの間反応がない場合はノードが停止しているものとみなされる
  2. 新しいリーダーの選出
    • 選出のプロセスが決められていたり、事前に指定されているコントローラノードによって選出されることもある
    • リーダーに相応しいのはリーダーの最新の変更に一番近いフォロワー
  3. システムの再設定
    • クライアントは書き込みリクエストを新しいリーダーに送信する
    • 以前のリーダーが復帰した際は、自身がリーダーのままだと勘違いしているので、降格させられていることを理解させる必要がある
    • また古いリーダーに新しいリーダーを認識させなければならない

フェイルオーバーの問題点

フェイルオーバーには問題になり得ることがある。

  • 非同期の場合、古いリーダーで他のフォロワーに書き込まれていない変更が存在する場合がある。古いリーダーが復帰した時に既に新しいリーダーに競合する書き込みを受信している可能性がある。こういった場合は単純に破棄するというのが一般的だが、これは永続性に反するということになる。
  • 上記の古いリーダーのみにある変更の書き込みを破棄する場合に、DBが他システムと連携している場合、整合性が失われる
  • リーダーが2つあるような状態に陥ってしまい、データの損失や破損が発生する可能性が高くなる
  • リーダーが落ちていると判断するためのタイムアウトはどれくらいにするのが適切か?タイムアウトを長くすればリーダーの生涯からリカバリする時間が長くなる。だからと言って短くすれば不要なフェイルオーバーが発生してしまう可能性もある。

フェイルオーバーには様々な問題があるため、自動フェイルオーバーをサポートしているソフトウェアでも運用チームによっては手動で行うことを好む場合もある。

レプリケーションログ

レプリケーションの動作はどのようになっているか?

ステートメントベースのレプリケーション

最もシンプルな方法。リーダーは書き込みリクエストのステートメントをすべてログに記録し、そのステートメントをすべてのフォロワーに送信する。RDBの場合で言えばinsert, update, delete文をフォロワーに送信する。

ステートメントベースのレプリケーションには以下のような考慮事項がある。

  1. NOW()やRAND()などの非決定的な関数を呼ぶステートメントは、それぞれのフォロワーで違った結果を生み出す
  2. 自動インクリメントされる列がある場合、すべてのレプリカ上で完全に同じ順序で実行しなければならない
  3. ストアドプロシージャやユーザー定義関数などその内部で非決定的なものが使われていると副作用をもたらす

上記のような問題は回避可能であるが、現在はステートメントベースのレプリケーションはあんまり使われていないっぽい。

論理ログレプリケーション

行ごとにコピーするようなイメージでいるが、理解が正しいか核心が持てない。

トリガベースレプリケーション

DB側ではなくアプリ側でレプリケーションする。データの一部だけレプリケーションしたい場合や異なる種類のDB間でレプリケーションを行う場合などはレプリケーションの処理をアプリケーションレイヤーに引き上げる必要がある。

通常この方法は推奨されないが、柔軟性が高いため有益な場合がある。

レプリケーションラグ

非同期でレプリケーションをしている場合、どうしてもリーダーとフォロワーの間にラグが生じる。通常1秒にも満たないラグであっても、場合によってはネットワークの問題などで数秒以上のラグが生じてしまうこともある。

レプリケーションのラグによって生じる問題には次のようなものがある。

  1. データ反映されていないと勘違いする
    • 多分一番に出てくる問題
    • あるユーザーが更新したのにも関わらず、データを取得すると更新が反映されていない状態
    • これにはread-afterwrite一貫性といって、ユーザーが自分で更新できるデータを読み込みなおしたときに必ず更新したデータを表示することを保証すること
  2. モノトニックな読み取り
    • 1度目のリクエストと2度目のリクエストで同じリクエストを違うフォロワーに取得リクエストをする際に発生する
    • 1度目のリクエストをしたフォロワーより、2度目のリクエストをしたフォロワーが遅れている場合、1度は見れた更新後のデータが、2度目は見れなくなってしまう
    • 同じユーザーの読み取りリクエストは同じフォロワーで処理する
  3. 一貫性のあるプレフィックス読み取り
    • 順序が逆転してしまうような問題
    • これはパーテーション化されたDBで問題になる

めも

  • リーダーレスレプリケーションについては後日