アプリケーションの成長に伴い、単一のデータベースサーバーでは処理しきれないデータ量やトラフィックに直面する場面が増えてきます。そこで登場するのがシャーディング(水平分割)という手法です。シャーディングとは、データを複数のデータベースインスタンスに分散配置することで、個々のサーバーにかかる負荷を軽減し、システム全体のスケーラビリティを確保する設計パターンです。
筆者はこれまで複数のプロジェクトでシャーディングの設計・運用に携わってきましたが、正直に言えば「導入しなくて済むなら導入しないほうがよい」技術のひとつです。それでも必要になるケースは確実に存在し、その際に正しい設計原則を知っておくことが、運用の成否を大きく分けます。
シャーディングの導入を検討すべきタイミングは、以下のような状況が重なったときです。
逆に言えば、これらの条件を満たしていないのにシャーディングを導入するのは、複雑さに対してメリットが見合わない過剰設計になりがちです。まずはインデックスの最適化、クエリチューニング、キャッシュ戦略の見直しを先に行うべきです。
シャーディング設計において、最も重要な意思決定はシャーディングキーの選定です。シャーディングキーとは、データをどのシャードに配置するかを決定するためのカラムのことです。この選択を誤ると、後から修正するのは非常に困難になります。
良いシャーディングキーの条件は以下の通りです。
たとえばECサイトであれば、ユーザーIDをシャーディングキーにするのが一般的です。ユーザーに紐づくデータ(注文履歴、カート、お気に入りなど)が同じシャードに格納されるため、ユーザー単位の操作はシャードをまたがずに完結します。
シャーディングキーの値の範囲によってデータを分割する方式です。たとえばユーザーIDが1〜100万はシャード1、100万1〜200万はシャード2、という具合です。理解しやすく実装もシンプルですが、データの偏りが発生しやすいという欠点があります。新規ユーザーが最新のシャードに集中するため、特定シャードへの書き込み負荷が高くなりがちです。
シャーディングキーにハッシュ関数を適用し、その結果によってシャードを決定する方式です。データの分散が均等になりやすいのが大きな利点です。ただし、レンジクエリ(範囲検索)の効率が悪くなるというトレードオフがあります。
どのデータがどのシャードにあるかのマッピングを別のサービスやテーブルで管理する方式です。柔軟性が最も高いですが、ルックアップのオーバーヘッドが発生し、マッピングテーブル自体が単一障害点になるリスクがあります。
シャーディングを導入すると、避けて通れないのがクロスシャードクエリの問題です。たとえば「全ユーザーの注文数ランキング」のようなクエリは、全シャードにクエリを発行して結果をマージする必要があります。
実務的な対処方法としては、以下のアプローチがあります。
いずれの方法も結果整合性を許容する設計が前提となります。強い整合性が必要な処理は、できる限り単一シャード内で完結させることが鉄則です。
ビジネスの成長に伴い、シャード数を増やす必要が出てくることがあります。これがリシャーディングです。ハッシュベースの場合、シャード数を変えるとハッシュの分配先が変わるため、大量のデータ移行が必要になります。
この問題を軽減するために、コンシステントハッシングの採用が有効です。シャード追加時に移行が必要なデータ量を最小限に抑えることができます。また、最初から十分な数の仮想シャード(バーチャルシャード)を用意しておき、物理サーバーへのマッピングを変更するアプローチも実践的です。
シャーディング環境の運用では、以下の点を特に意識しています。
シャーディングは銀の弾丸ではなく、運用コストと複雑性を大幅に増加させるトレードオフを伴う技術です。導入の判断は慎重に、しかし必要だと判断したら設計原則に忠実に進めることが、長期的な運用の安定につながります。