シャーディング
データを複数のデータベースインスタンスに水平分割し、書き込みスループットとストレージ容量をスケールさせる手法
シャーディングとは
シャーディング (Sharding) は、データを複数のデータベースインスタンス (シャード) に水平分割し、書き込みスループットとストレージ容量をスケールさせる手法である。1 台の DB では処理しきれないデータ量やトラフィックに対応する。
シャーディング前: 1 台の DB に全データ
シャーディング後:
シャード 0: userId 0〜999
シャード 1: userId 1000〜1999
シャード 2: userId 2000〜2999
シャーディング戦略
分割の質を決めるのはシャードキーの選び方である。値ごとにアクセスが偏らないこと、そして 1 つのキーで引ける範囲がアプリケーションのクエリ単位と一致していることの 2 つが条件になる。この 2 つは同時に満たしにくく、どちらを優先するかで次の 4 方式に分かれる。
| 戦略 | 仕組み | メリット | デメリット |
|---|---|---|---|
| ハッシュ | キーのハッシュ値で分割 | データが均等に分散 | 範囲クエリが非効率 |
| レンジ | キーの範囲で分割 | 範囲クエリが効率的 | ホットスポットが発生しやすい |
| ジオベース | 地理的に分割 | レイテンシ最適化 | 地域間クエリが複雑 |
| ディレクトリ | ルックアップテーブルで分割先を決定 | 柔軟な分割 | ルックアップが単一障害点 |
レンジ方式でホットスポットが起きるのは、時系列に増えるキー (連番 ID・日付) を範囲で切ると、新しく書き込まれる行が常に最後のシャードへ集まるためである。書き込みが 1 台に集中する一方で、残りのシャードは容量を占めるだけで遊ぶ。ハッシュ方式はこの偏りを避けられるが、ハッシュ後の値は元の順序を失うため「直近 1 週間の注文」のような範囲クエリが全シャード走査になる。読み取りが範囲中心なら、キーの上位に取りうる値の多い項目 (テナント ID 等) を置き、その内側で時系列順を保つ複合キーにするのが現実的な折衷になる。
DynamoDB の自動シャーディング
DynamoDB は内部的にシャーディングを自動で行う。パーティションキーのハッシュ値でデータを分散し、パーティションの管理はサービス側が担う。パーティションが追加されるのは、既存のパーティションが支えられる量を超えてスループットを引き上げたときと、既存のパーティションが容量いっぱいになってさらに領域が必要になったときである (2026 年 8 月時点の公式ドキュメント)。1 つのパーティションが出せる上限は 1 秒あたり読み込み 3,000 ユニット・書き込み 1,000 ユニットで、テーブル全体の設定値をどれだけ大きくしても、単一のパーティションキーへ集まったアクセスはこの壁で止まる。グローバルセカンダリインデックスも同じくパーティションで構成されるため、インデックス側のキー設計にも同じ偏りの問題が起きる。
// パーティションキーの設計が重要
// ❌ 悪い例: 日付をパーティションキーにすると、今日のパーティションに集中
{ PK: '2026-04-01', SK: 'order-123' }
// ✅ 良い例: userId をパーティションキーにすると、ユーザーごとに分散
{ PK: 'user-456', SK: 'order-123' }
RDS vs DynamoDB のシャーディング
両者の差は「分割の責任を誰が持つか」に集約される。RDS では分割ルールもルーティングもアプリケーションのコードに残り、シャードを増やすたびにその両方を書き換える。DynamoDB では分割の実行がサービス側へ移るが、どのキーで分けるかという判断は利用者に残る。
| 観点 | RDS | DynamoDB |
|---|---|---|
| シャーディング | 手動 (アプリケーション側) | 自動 (パーティションキー) |
| リバランス | 手動 | 自動 |
| クロスシャードクエリ | 複雑 (全シャードに問い合わせ) | GSI で別のキーから引ける (任意の集計は不可) |
| トランザクション | 分散トランザクション (2PC) が必要 | TransactWriteItems (最大 100 アクション・合計 4 MB) |
シャーディングの課題
クロスシャードクエリ
複数のシャードにまたがるクエリ (JOIN、集計) は、全シャードに問い合わせて結果をマージする必要がある。
「全ユーザーの注文合計」→ 全シャードに問い合わせ → 結果をマージ
「userId=123 の注文」→ シャード 0 だけに問い合わせ (効率的)
リシャーディング
データ量の増加に伴いシャード数を増やす場合、既存データの再配置が必要になる。単純な剰余 (userId % シャード数) で分けていると、シャードを 3 台から 4 台へ増やすだけでほとんどの行の配置先が変わる。コンシステントハッシュを使えば、移動するのは新しいシャードが引き受ける範囲のデータだけに抑えられる。
抑えられるのは移動量であって、移行の手間ではない。稼働中のサービスで再配置するには、旧シャードと新シャードへの二重書き込み、過去データのバックフィル、両者の一致確認、読み取り先の切り替え、旧データの削除という段取りを踏む。その間はアプリケーションが 2 つの配置ルールを同時に抱えるため、シャード数を 1 段増やす作業が数日から数週間の案件になる。そのため実務では物理シャードより多くの論理シャードにあらかじめ分けておき、増設時は論理シャードごと新しい物理シャードへ引っ越す設計を採ることが多い。
トランザクション
複数シャードにまたがるトランザクションは、分散トランザクション (2PC) が必要になり、複雑さとレイテンシが増大する。
シャーディングを避ける方法
シャーディングは複雑さを大幅に増すため、他の手段で対応できないか先に検討する。
- リードレプリカ: 読み取り負荷の分散
- キャッシュ (ElastiCache): 頻繁に読まれるデータをキャッシュ
- インデックスの最適化: クエリパフォーマンスの改善
- DynamoDB: パーティショニングをサービス側に任せる
DynamoDB を選べば、分割ルールの実装とリバランスの運用は手放せる。ただし分割そのものが消えるわけではない。アクセスが 1 つのパーティションキーへ集まれば前述の上限に当たり、書き込みを散らすためにキーの末尾へ乱数や連番を付ける ホットパーティション 対策を自分で書くことになる。手放せるのは実装と運用で、キー設計の責任は残る。
つまずきやすい点
シャーディングを入れると、それまでデータベースが引き受けていた仕事がアプリケーション側へ戻ってくる。自動採番の主キーはシャードをまたいで一意にならないため UUID か採番用の仕組みが必要になり、外部キー制約と JOIN はシャードをまたいだ時点で効かなくなる。集計クエリは全シャードへの問い合わせとマージになり、シャードごとに応答時間がばらつくので、いちばん遅いシャードが全体のレイテンシを決める。
もう 1 つの落とし穴は、運用作業がシャードの台数分に増えることである。スキーマ変更・バックアップ・バージョンアップはシャードごとの実施になり、途中で失敗するとシャード間でスキーマが食い違った状態が残る。導入を決める前に、水平スケーリング の他の手段を使い切ったか、再配置の負担を コンシステントハッシュ で軽くできるかを確認しておきたい。
この記事は役に立ちましたか?
関連用語
コンシステントハッシュ
ノードの追加 / 削除時にデータの再配置を最小限に抑える分散ハッシュアルゴリズム
水平スケーリング
サーバーの台数を増やして処理能力を向上させるスケーリング手法
DynamoDB
AWS のフルマネージド NoSQL データベースで、ミリ秒単位のレイテンシとシームレスなスケーリングを提供する
ホットパーティション
特定のパーティションにアクセスが集中し、スループットが低下する DynamoDB の性能問題
Kinesis
AWS のリアルタイムデータストリーミングサービスで、大量のデータを収集 / 処理 / 分析する
バックプレッシャーとは - ストリーム処理の流量制御を図解で理解する
バックプレッシャーは下流の処理能力を超えるデータ流入時に上流へ減速を伝える仕組み。Node.js Stream/RxJS/Kafka での実装パターンと OOM 回避策を解説