書き込み分散は難しい
読了目安 約4分
レプリケーションで読みを増やし、シャーディングで書き込みを分け、その代償に何を失うかを CAP 定理まで含めて整理します。
この章の目次
アプリは 台数を増やすと何が起きるか の手で増やせました。 最後に残るのが、正であるデータベースの増やし方です。
読みはレプリカで増やせる
レプリケーションは、正とするサーバー(プライマリ)の変更を別のサーバー(レプリカ)へ流し続け、同じ中身のコピーを保つ仕組みです。 読みのクエリをレプリカに散らせば、読みの容量は台数ぶん増えます。
書き込みは増えません。 変更の順序を 1 か所で決めるため、書き込みはプライマリ 1 台に集まったままです。
変更がレプリカに届くまでにはわずかな遅れがあり(レプリケーション遅延)、書いた直後にレプリカから読むと自分の投稿がまだ見えないことがあります。 「書いた本人の直後の読みだけプライマリへ向ける」が定番の逃げ道です。
書き込みを分けるのがシャーディング
シャーディングは、データをキーで分割して、正とするデータベースそのものを複数台に分けることです。 ユーザー ID の範囲やハッシュで、行の置き場所を決めます。
代償は大きく、境界をまたぐ JOIN とトランザクションが使えなくなります。 道具は問い合わせの形で選ぶ の万能型の能力を、境界の上では失います。 分け直しも高くつくので、書き込みの飽和が計測で確定してから選ぶ最後の手です。
止まるか、ずれるか
サーバーが 2 台以上になった系に望みたい性質は 3 つあります。
- 一貫性:どのサーバーに聞いても、同じ答えが返る
- 可用性:いつ聞いても、答えが返ってくる
- 分断耐性:サーバー間の通信が切れても、系全体は動き続ける
3 つ同時には成り立ちません。 プライマリとレプリカの間の通信だけが切れ、その間にレプリカへ来た読みを考えると分かります。
答えを返すなら、プライマリで起きたかもしれない更新を知らないまま答えるので、一貫性を捨てています。 ずれた答えを出すまいと黙るなら、可用性を捨てています。 どちらも嫌なら、通信が切れない世界が必要ですが、切断は物理で起きるので選べません。
残るのは「分断のあいだ、止まるか、ずれるか」の二択です。 これを CAP 定理と呼びます。
ここでの「一貫性」は、トランザクションの ACID 特性の一貫性とは別物です。 あちらは 1 台の中でデータが守るべき決まりが壊れないこと、こちらは複数台のどれに聞いても同じ答えが返ることです。
| 分断のあいだ | 捨てたもの | 実際の構成の例 |
|---|---|---|
| 書き込みを止める | 可用性 | etcd、ZooKeeper(分散システムの調停役)、Galera Cluster(MySQL を同期レプリケーションで組む構成)、Spanner |
| 古くても答え続ける | 一貫性 | リードレプリカ構成(MySQL の非同期レプリケーション)、Valkey の Sentinel 構成、Cassandra |
何を捨てるかは用途が決めます。 ロックやリーダー選出を預かる etcd や ZooKeeper が一貫性側に立つのは、決め方を誤ると分断の両側が「自分は書き込める」と思い込み、両方が受け付けてしまうからです(スプリットブレイン)。 読みが古いだけより深刻な、後から合わせられないデータの食い違いです。 奇数台で組むのは、過半数を取れた側だけが書き込みを続ける多数決で、これを防ぐためです。
では、分断耐性を捨てた構成はどれでしょうか。 分散をやめた 1 台のデータベースです。 中に通信の切断はないので、箱が壊れるまでは一貫性と可用性を両立できます。 Part 6 までデータベースが 1 台だったのは、この隅に立っていたからです。
データをどこに置くか で「正にできるのはデータベースだけ」と線を引いたのは、この二択の言い換えです。 正は止まってでも正しく、コピーはずれても答え続けます。
ISUCON でここまで組む時間はまずなく、支給された台数なら分離までで足ります。 ここから先は、本番の Web サービスを育てるときの地図です。
試してみよう
次の 3 つの症状に、この章の手を当ててください。
- 読みが 9 割のサービスで、データベースの CPU が飽和した
- 書き込みが 9 割のサービスで、データベースの CPU が飽和した
- 投稿した直後に一覧へ戻ると、たまに自分の投稿だけ見えない
答えを見る
1 はレプリカを足して読みを散らします。読みが主役なら、容量は台数ぶん増えます。 2 にレプリカは効きません。書き込みはプライマリ 1 台に集まったままだからです。先にサービス需要を削り、それでも書き込みが飽和するときだけシャーディングを検討します。 3 はレプリケーション遅延です。故障ではないので、書いた本人の直後の読みだけプライマリへ向けます。