シャーディング — データを分けて持つ

1台に入りきらないデータは、複数台で分担する。どうやって分けるか(ハッシュ分割・範囲分割)、台数が変わったらどう引っ越すか(リバランス)。分散データベースの土台になる考え方を、キーが実際に散らばる様子で体感します。

1. キーで行き先を決める — ハッシュ分割

データ全体をいくつかのシャード(断片)に分け、各ノードが一部だけを担当する方式をシャーディング(パーティショニングとも)と呼びます。いちばん基本的な分け方がハッシュ分割:キー(ユーザー名など)からハッシュ関数で大きな数を作り、ノード台数で割った余りで行き先を決めます。

下のデモで「キーを追加」を連打してみてください。行き先はキーごとにバラバラに見えて、数が増えるほど4台にほぼ均等に散らばっていきます。

hash(key) mod 4 — キーが4台に散らばる
名前つきの粒=キー。中央の hash(key) mod 4 を通ると担当ノードが決まります。個々の行き先は予測しにくいのに、全体はほぼ均等になる — これがハッシュ分割の狙いです(左上に最多と最少の差を表示)。
担当ノード = hash(key) mod N N = ノード台数。hash はキーから大きな整数を作る関数。どのノードが担当かは「計算するだけ」で分かり、台帳への問い合わせが要らない
POINT — ハッシュ分割が愛される理由 似たキー(user100 と user101)でもハッシュ値は全く別になるため、どんなキーの偏りでもほぼ均等にばらまける。さらに担当ノードは式ひとつで決まるので、どこに聞けばよいか迷わない。

2. もう1つの分け方 — 範囲分割とホットスポット

辞書の巻のように「a〜f はノード0、g〜m はノード1…」とキーの範囲で分けるのが範囲分割です。隣り合うキーが同じノードに集まるので、「a で始まる商品を全部読む」のような連続アクセス(範囲スキャン)に強いのが利点。

ただし弱点があります。下のデモで「アクセスの偏り」スライダーを上げて、人気キー news にアクセスが集中したときに何が起きるか見てください。

範囲分割 — 人気キーで1台だけ過熱する(ホットスポット)
落ちてくる粒=読み書きのリクエスト。偏りを上げると人気キー news(n〜s 担当のノード2)だけにリクエストが集中し、そのシャードだけ負荷バーが赤く振り切れます。他の3台が暇でも、システムの体感速度は過熱した1台で決まってしまいます。
注意 — ホットスポットはハッシュ分割でも起きる ハッシュ分割にしても、「特定の1つのキー」への集中は同じシャードに落ち続ける(同じキーのハッシュ値は毎回同じだから)。有名人のタイムラインなど極端な人気キーには、キー末尾に乱数を足して複数シャードへ書き分けるなど、アプリ側の工夫が必要になる。

3. ノードを増やす日 — リバランスの大問題

データが増えたので4台を5台にしたい。ここで mod N 方式の落とし穴が現れます。N が 4→5 に変わると、ほぼすべてのキーで余りの値が変わり、大量のデータ引っ越しが発生するのです。

そこで考え出されたのがコンシステントハッシュ。ハッシュ値を円環(リング)上の位置とみなし、キーは「リングを時計回りに進んで最初に出会うノード」が担当します。ノードを追加しても、新ノードのすぐ手前の区間のキーだけが引っ越します。下の「ノードを追加」ボタンで両方式の移動量を比べてください。

リバランス対決 — mod N 方式 vs コンシステントハッシュ
点=キー180個。色=担当ノード。ノードを追加すると、移動したキーが白くフラッシュします。左の mod N 方式はほぼ全キーの色が変わるのに対し、右のリングは新ノードの手前の一区間だけ。移動数のカウントで差は一目瞭然です。
リング方式の移動量 ≈ K / N 個 K = 総キー数、N = 追加後のノード数。mod N 方式では約 K × (1 − 1/N) 個 — つまりほぼ全部が動いてしまう
一歩先へ — 実際のシステムでは Cassandra や Amazon DynamoDB はコンシステントハッシュに仮想ノード(1台をリング上のたくさんの点に分身させ、担当区間の偏りを均す技法)を組み合わせている。Redis Cluster は 16384 個の「ハッシュスロット」を台数で分け合う方式、MongoDB はハッシュ分割と範囲分割を用途で選べる。分け方は違っても「追加・削除時の移動を最小にする」という狙いは共通だ。

4. 現実の構成 — シャーディング × レプリケーション

シャーディングは「分けるだけ」なので、1台壊れるとそのシャードのデータが丸ごと読めなくなる危険があります。だから実際のシステムでは、各シャードを複数のノードに複製(レプリケーション)して持ちます。「分けて、さらに複製する」— これが分散データベースの標準形です。

シャード4 × レプリカ3 の格子 — 故障しても全データ健在
各マシンは3つのシャードの複製を預かり、各シャード(S0〜S3)は3台に分散配置されています。落ちてくる粒=読み取りリクエストで、生きている複製へ自動で流れます。1台、2台と故障させても全シャードが読めることを確かめてください。3台目で何が起きるかも要チェック。

5. まとめ