🔩 ねじき教室 Go と Web の教室

書き込み分散は難しい

読了目安 約5分

レプリケーションで読みを増やし、シャーディングで書き込みを分ける仕組みと、その代償を CAP 定理まで含めて整理します。

30秒でつかむ 読みを複製するか、書き込み先を分けるか
読みを複製するか、書き込み先を分けるかレプリケーションは読み取り先を増やし、シャーディングはキーごとに書き込み先を分けます。1レプリカ読みを増やす2データずれを扱う3シャード書きを分ける
  1. レプリカ 読みを増やす
  2. データ ずれを扱う
  3. シャード 書きを分ける
この章の目次

アプリは 台数を増やすと何が起きるか の手で増やせました。 最後に残るのが、正であるデータベースの増やし方です。

読みはレプリカで増やせる

レプリケーションは、正とするサーバー(プライマリ)の変更を別のサーバー(レプリカ)へ流し続け、同じ中身のコピーを保つ仕組みです。 読みのクエリをレプリカに散らせば、レプリカの処理能力やクエリの偏りが次のボトルネックになるまで、読みの容量を増やせます。

ここで扱う単一プライマリ構成では、書き込みの容量は増えません。 変更の順序を 1 か所で決めるため、書き込みはプライマリ 1 台に集まったままです。

変更がレプリカに届くまでにはわずかな遅れがあり(レプリケーション遅延)、書いた直後にレプリカから読むと自分の投稿がまだ見えないことがあります。 「書いた本人の直後の読みだけプライマリへ向ける」が定番の逃げ道です。

書き込みを分けるのがシャーディング

シャーディングは、データをキーで分割して、正とするデータベースそのものを複数台に分けることです。 ユーザー ID の範囲やハッシュで、行の置き場所を決めます。

アプリケーション側で単純にシャーディングすると、境界をまたぐ JOIN やトランザクションをデータベース 1 台だけでは処理できません。 複数のシャードから結果を集めたり、分散トランザクションで調停したりする必要があるため、実装と運用が難しくなり、通信の費用も増えます。 ツールはアクセスパターンで選ぶ で扱った万能型の能力を保てる分散データベースもありますが、その仕組みは次章で扱います。 分け直しも高くつくので、書き込みの飽和が計測で確定してから選ぶ最後の手です。

止まるか、ずれるか

複数のサーバーにデータを複製する系では、CAP 定理に出てくる 3 つの語を区別する必要があります。

  • 一貫性:すべての読み書きが、実時間の順序を守る 1 つのコピーに対して行われたように見える(線形化可能性)
  • 可用性:故障していないサーバーが受け取ったすべての要求に、最終的にエラーではない応答を返す
  • 分断耐性:サーバー間のメッセージが失われ、互いに通信できない状態でも、系に求めた保証を保つ

CAP 定理が述べるのは、ネットワーク分断が起きている間、一貫性と可用性を同時には保証できないということです。 2 台のサーバー間の通信が切れ、左側が書き込みを受け付けたあと、右側に読みが来た場合を考えると分かります。

右側が手元の値を返すと、その読みは左側で完了した最新の書き込みを反映できず、一貫性を保証できません。 最新の値か確認できるまで待つか、エラーを返すと、すべての要求に正常な応答を返す可用性を保証できません。

残るのは「分断のあいだ、止まるか、ずれるか」の二択です。 ネットワーク分断は実際に起こり得る障害条件なので、設計者が都合よく捨てられる機能ではありません。

ここでの「一貫性」は、トランザクションの ACID 特性の一貫性とは別物です。 あちらはトランザクションの前後でデータが守るべき制約を保つこと、こちらは並行した読み書きが 1 つのコピーに対する操作のように見えることです。

分断のあいだの方針保証できない性質起きること
一貫性を優先する可用性最新値を確認できない側の要求を待たせるか、エラーにする
可用性を優先する一貫性分断の両側で処理を続け、復旧後に差分や競合を解消する

どちらを優先するかは、系全体で固定するとは限りません。 読みと書きで方針を変えたり、要求の種類ごとに整合性の強さを選べたりするデータベースもあります。

ロックやリーダー選出を預かる仕組みは、一般に一貫性を優先します。 分断の両側が同時にリーダーとして書き込みを受け付ける スプリットブレインを防ぐため、過半数のサーバーと通信できる側だけが処理を続けます。 この多数決に必要なのが クォーラムです。 3 台と 4 台はどちらも 1 台の故障までしか耐えられないため、同じ故障耐性を少ない台数で得られる奇数台がよく使われます。

1 台だけのデータベースは、複製されたデータストアを対象にする CAP 定理の比較から外れます。 ネットワーク分断を避けられても、その 1 台が故障すれば利用できなくなるので、一貫性と可用性を無条件に両立できるわけではありません。

データをどこに置くか では、更新を受け付ける「正」と、遅れて反映されてもよいコピーを分けました。 これはこの教材で採る設計上の区別であり、すべての分散データベースが同じ構成を採るという意味ではありません。

ISUCON でここまで組む時間はまずなく、支給された台数なら分離までで足ります。 ここから先は、本番の Web サービスを育てるときの地図です。

試してみよう

次の 3 つの症状に、この章の手を当ててください。

  1. 読みが 9 割のサービスで、データベースの CPU が飽和した
  2. 書き込みが 9 割のサービスで、データベースの CPU が飽和した
  3. 投稿した直後に一覧へ戻ると、たまに自分の投稿だけ見えない
答えを見る

1 はレプリカを足して読みを散らします。読みが主役なら、容量は台数ぶん増えます。 2 にレプリカは効きません。書き込みはプライマリ 1 台に集まったままだからです。先にサービス需要を削り、それでも書き込みが飽和するときだけシャーディングを検討します。 3 はレプリケーション遅延です。故障ではないので、書いた本人の直後の読みだけプライマリへ向けます。