シャーディング
データを分割キーで複数ノードに水平分割し、単一ノードの限界を超えて格納・処理する手法。
概要
シャーディング(sharding)は、1つの大きなデータの集まりを「分割キー」に従っていくつかの塊(シャード)に切り分け、それぞれを別々のノード(サーバ)に分けて格納する手法です。「割れた破片(shard)」という語のとおり、たとえばユーザー情報を「ユーザーIDの範囲」や「IDのハッシュ値」で分け、AさんのデータはノードA、BさんのデータはノードBへ、というように振り分けます。こうすると、1台では収まりきらない量のデータを複数台に分散して持て、読み書きの負荷も台数に応じて分散します。
これはスケーリングの中でも、サーバの性能を上げる「垂直スケール(スケールアップ)」ではなく、台数を増やして負荷を分ける「水平スケール(スケールアウト)」をデータ層で実現する代表的な方法です。データベースの文脈では「水平分割(horizontal partitioning)」とも呼ばれ、行を分割キーごとに別テーブル・別ノードへ振り分けます。サービスが成長し、1台のRDBMSではディスクもメモリもCPUも足りなくなったとき、真っ先に検討される選択肢の一つです。
なぜ生まれたか
サービスが小さいうちは、データベースは1台で足ります。足りなくなってきたら、まずはより高性能なマシンに載せ替える「スケールアップ」で凌げます。しかしこの手には天井があります。世界最速のサーバでも積めるメモリやディスクには上限があり、値段も性能の限界に近づくほど急激に跳ね上がります。データが数十テラバイト、書き込みが毎秒数万件という規模になると、どんな1台をもってしても物理的に処理しきれなくなります。
そこで「1台を強くする」のではなく「多数の平凡なマシンに分けて載せる」という方向に発想を変えたのがシャーディングです。データを分割キーで割り振れば、各ノードは全体の一部だけを担当すればよく、台数を増やすほど容量も処理能力も伸びます。もともとは大規模Webサービスが巨大RDBMSの限界を回避するために手作業で編み出した工夫でしたが、後のNoSQL系データストアはこの分散を最初から仕組みとして内蔵し、シャーディングを「設計時の前提」に格上げしました。
詳細
分割キーで振り分ける
シャーディングの成否は「何をキーに分けるか(シャードキー)」にほぼ集約されます。代表的な戦略は3つです。範囲分割(レンジ)は「ID 1〜1000万はノード1、1000万〜2000万はノード2」のようにキーの範囲で分けます。範囲検索が得意な反面、新しいIDが特定ノードに偏る「ホットスポット」が起きやすくなります。ハッシュ分割は、キーのハッシュ値でノードを決めます。分布は均等になりますが、範囲検索が全ノードに散らばるのが弱点です。ディレクトリ分割は「どのキーがどのノードにあるか」を別の索引で管理し、柔軟に配置できる代わりに索引自体が要になります。
クライアントの読み書きは、まず「そのキーがどのシャードにあるか」を判定するルータ(プロキシやドライバ)を通り、担当ノードへ振り分けられます。1件のキー指定の読み書きは1ノードで完結するため速いのですが、シャードをまたぐ検索や集計は全ノードへ問い合わせて結果を束ねる必要があり、遅く複雑になります。
レプリケーションとの違い
混同しやすいのがレプリケーションとの違いです。両者はどちらも「複数のノードにデータを置く」ものですが、目的も置き方も正反対です。レプリケーションは同じデータの複製を各ノードに丸ごと持たせ、可用性と読み取り性能を高めます。1台が落ちても他が同じデータを持っているので生き残れますが、全体の容量は1台ぶんのままです。シャーディングは各ノードに異なる一部だけを持たせ、容量と書き込み性能を伸ばします。その代わり、あるシャードが落ちるとその範囲のデータだけ丸ごとアクセス不能になります。
このため実務では両者を組み合わせます。データをシャードで分割したうえで、各シャードをさらにレプリケーションで複製する——こうして容量・性能・可用性を同時に確保するのが、大規模データストアの標準的な構成です。
落とし穴と設計のトレードオフ
シャーディングは強力ですが、導入した瞬間に多くの前提が崩れます。まず、複数シャードにまたがるトランザクションが極端に難しくなります。1台のRDBMSなら当たり前だった「複数行をまとめて原子的に更新」が、ノードをまたぐと分散トランザクションという別次元の問題になり、性能も設計コストも跳ね上がります。JOINや一意制約も、キーが別シャードに散っていると効かせにくくなります。
さらに厄介なのがシャードキー選びの後戻りの難しさです。偏ったキーを選ぶと特定シャードだけに負荷が集中する「ホットスポット」が生まれ、分散した意味が薄れます。かといって運用開始後にキーを変えたり、シャードを増減させてデータを再配置(リシャーディング)したりするのは、大量データの移動を伴う一大作業です。この痛みを和らげるため、ノードの増減時の移動量を抑える「コンシステントハッシュ法」や、あらかじめ細かい仮想シャードに分けておいて後で物理ノードへ割り当て直す設計が使われます。だからこそ鉄則は「必要になるまでシャーディングしない」こと。まずスケールアップとレプリカで粘り、それでも限界というときに初めて手を出すのが、複雑さに見合った判断です。