Lazyレプリケーションはpush型とpull型のどちらが良いのか
更新ログをマスタからスレーブに送ることでデータをレプリケーションする、いわゆるログシッピングを、分散データストアに実装する方法について。
ログシッピングは、操作を同時に複数箇所に送信するレプリケーションと比べると、次のような実装上の利点がある:
- オリジナルに対する変更とレプリカに対する変更の適用順序が一致する
- オリジナルとレプリカの整合性を維持しやすくなる。設計が簡単になるので嬉しい。
- ダブってレプリケーションされない
- at-most-once。どこまで更新ログを受け取ったかをスレーブ側で管理している前提。
- CPUにやさしい
- バースト的に更新クエリが発生した時でも、レプリケーションクエリの数は(激しくは)増えない。その代わりログに溜まって一度に送信される。バッチ処理はCPUにやさしく、スループットが高い。
一方、更新ログを管理しなければならない欠点があるが、ストレージのデータ構造自体がログであれば不要にできるだろう。
ここで、このレプリケーションを
- push型
- マスタがスレーブに更新ログを投げつける。
- pull型
- スレーブがマスタから更新ログを取り寄せる。
のどちらで実装するのが良いのか。
push型を実装するには、次に送るべきデータをマスタが知っている必要がある。
pull型を実装するには、次に受け取るべきデータをスレーブが知っている必要がある。
前者の場合、マスタは "スレーブが持っていないデータ" を送れば良い。マスタは "スレーブがどこまでデータを持っているのか" を知っている必要がある。
後者の場合、スレーブは "自分が持っていないデータ" を受け取れば良い。スレーブは "自分がどこまでデータを持っているのか" を知っている必要がある。
後者ならステートレスなプロトコルで実装できるが、前者はステートフルになってしまう。
その上に、分散データストアでは、マスタに対するスレーブが変化することがある:
- スレーブが故障したあと復旧した
- マスタが故障したあと復旧した
- 新しいスレーブを追加した
これらの変化を人間に対処させるのは良い割り切りだが、MySQLになるので避けたい。
しかし、こういう変則的な変化をステートフルに管理するコードは、たいていバグるので良くない。
以上の理由で、pull型の方が良いのではないかと思っているところ。
バグを生み出さない最善の方法はプログラムを書かないことであって、そもそもレプリケーションなんて自分で書かないのが一番良いに決まっている。