NoSQLと分散DB — 1台の限界を超える

データが1台のサーバに収まらなくなったとき、道は2つ。「もっと大きな1台」を買い続ける(垂直)のではなく、安いサーバを何台も横に並べて分担させる(水平スケール)。この発想の転換が、きっちりした表とJOINを少し諦めたNoSQLと、複数ノードにデータを撒く分散DBを生みました。用途ごとのデータモデル、横分割(シャーディング)、そして分散の宿命 CAP定理 を、動かしながら見ていきます。

1. 一つの正解から、用途ごとの形へ — データモデル動物園

リレーショナルDBは「すべてを行と列の表で表す」一つの強力なモデルです。でも、SNSのつながり・巨大なログ・自由な構造のJSON… 用途によっては表が窮屈になる。NoSQLは「表以外の形」を積極的に選びます。下のボタンで、同じ「ユーザーと注文」データが各モデルでどう見えるかを切り替えてください。

データモデル動物園 — 同じデータ、5つの表し方
表=きっちりした行列とJOIN、キーバリュー=キーで一発、ドキュメント=入れ子JSONをそのまま、列指向=列ごとに格納、グラフ=つながりを直接たどる。青いボタンが今のモデルです。
POINT — NoSQL=「Not Only SQL」 NoSQL は「SQLを否定する」ではなく「SQLだけじゃない」。リレーショナルが不要になったのではなく、適材適所で道具が増えた、と捉えるのが正確。キャッシュはKVS、商品カタログはドキュメント、時系列ログは列指向、友達関係はグラフ ― のように使い分ける。

2. 横に増やして捌く — シャーディング(水平分割)

1台に収まらない大量の行は、シャードキー(例: ユーザーID)を関数にかけて、複数ノードに振り分けて置きます。これがシャーディング。読み書きが各ノードに分散するので、ノードを足せば足すほど捌ける量が増える(スケールアウト)。下でノード数を変えて、行がどのノードに落ちるか、そしてノードを増やしたときに何件が引っ越すかを見てください。

シャーディング — キーでノードに振り分ける/再配置
上から降ってくるのが新しい行(キー)。shard = key mod N で落ちるノードが決まります。Nを変えると、割り当てが変わった行が新しいノードへ引っ越します(=再配置)。単純な mod だと、Nを1つ変えるだけでほとんどの行が動いてしまうことに注目。
1ノードあたりの行数 ≈ 全行数 ÷ N Nを増やすほど各ノードは軽くなる。ただし「単純mod」は再配置が爆発するため、実運用ではコンシステントハッシュ(増減時に動く鍵を 1/N 程度に抑える)を使う
注意 — 「横に並べれば速い」わけではない シャードをまたぐJOINや集計、複数ノードにわたるトランザクションは途端に難しく・遅くなる。だからシャードキーは「1台で完結する問い合わせが多くなる」ように選ぶのが鉄則。キー選びを誤ると、特定ノードに負荷が集中するホットスポットが生まれる。

3. 分断のとき、C か A か — CAP定理

ノードを増やすと必ず起きるのがネットワーク分断(Partition) ― ケーブルが抜けたり、片方のデータセンターと連絡が取れなくなる。このとき、切り離された少数派ノードに読み書きが来たらどうする? 選択肢は2つしかありません。拒否して一貫性を守る(C)か、古い値でも応答して可用性を守る(A)か。下のボタンで分断を起こし、C と A を選んでみてください。

CAP定理 — 分断したクラスタで C か A を選ぶ
多数派は新しい値 v2 を持ちます。少数派に読み取り要求(オレンジの矢)を送ったとき、CP=応答拒否(正しいが使えない)、AP=古い値 v1 を返す(使えるが間違い)。分断がない間はどちらも両立できることも確認してください。
POINT — CAPの正確な主張 よくある誤解は「C・A・P の3つから常に2つを選ぶ」。正しくは ― 分断(P)が起きている間だけ、C と A のどちらかを諦める。分断は起こるか起こらないかを選べない(ネットワークの事実)ので、実質「分断時に CP か AP か」の選択になる。分断がなければ両方満たせる。
分断時: 一貫性 C ⟷ 可用性 A (両立不可) より精密には PACELC: 分断時(P)は C か A、そうでない時(E)も 遅延(L) か 一貫性(C) のトレードオフがある、と拡張される

4. 「やがて揃う」という妥協 — 結果整合性

可用性(AP)を選ぶと、書き込み直後はノードごとに値が食い違う瞬間が生まれます。でも普通は放っておくと更新が全ノードへ伝わり、いずれ全員が同じ値に落ち着く。これが結果整合性(Eventual Consistency)。強整合性(常に最新)ほど厳しくない代わりに、速くて落ちにくい。下で伝播の遅さを変え、更新が波及する間の「不整合ウィンドウ」と、読み取りが古い値を拾ってしまう様子を見てください。

結果整合性 — 更新が波及し、やがて収束する
リーダーが新しい版 verを受け取ると、各レプリカへ非同期にコピーが飛びます。まだ届いていないレプリカは赤(古い)、届いたら緑(最新)。下の読み取りは、たまたま古いレプリカに当たると古い版を返します。伝播が遅いほど、この「不整合ウィンドウ」が長くなります。
一歩先へ — ACID から BASE、そして揺り戻し リレーショナルの厳格さ(ACID)に対し、NoSQL/分散が掲げたのが BASE(Basically Available, Soft state, Eventually consistent)― 少し緩めて可用性とスケールを取る思想。だが「結果整合はアプリが難しくなる」反省から、いまはスケールしつつ強整合・トランザクションも取り戻す NewSQL や分散SQL(Spanner・CockroachDB 等)が主流に。その心臓部にある2相コミットと合意アルゴリズムが、次のレッスンの主役です。

5. まとめ — スケールと引き換えに何を手放すか

一歩先へ — 「銀の弾丸はない」を設計に落とす NoSQLは万能薬ではなく、捨てるもの(JOIN・強整合・柔軟なクエリ)と得るもの(スケール・可用性・速度)を天秤にかける工学的判断だ。だから現場では「全部NoSQL」でも「全部RDB」でもなく、用途ごとに複数のDBを組み合わせる(ポリグロット・パーシステンス)のが普通。次は、複数ノードにまたがる更新を安全に確定させる分散トランザクションの世界へ。