コンテンツにスキップ

Database Sharding Explained: Strategies for Scalable SQL Performance

チェック

  • [ ] 本文を確認した
  • [ ] 概要を確認した
  • [ ] タグを確認した
  • [ ] inbox/ 直下へ移行した

概要

データベースシャーディングの定義、パーティショニングとの違い、主要な分割戦略、導入すべきケースと避けるべきケース、実装方式、導入手順を整理した記事。 単一データベースの性能限界を超えるためにデータを複数ノードへ分散するのがシャーディングだが、クエリ、トランザクション、運用、再配置の複雑性が増える。 TiDB のような分散 SQL データベースは、その複雑性の一部をデータベース側に寄せる選択肢として紹介されている。

本文

シャーディングは、一つの論理データセットを複数の物理ノードに分割して保持する手法。 各シャードはデータ全体の一部を持ち、全シャードを合わせると完全なデータセットになる。 多くの場合、各行は一つのシャードにだけ存在する。

単一データベースは、CPU、メモリ、ディスク I/O、接続数、ストレージ容量の上限にぶつかる。 より大きなマシンに移す垂直スケールは分かりやすいが、コストが高く、上限もある。 シャーディングは、データと負荷を複数マシンへ分散し、水平スケールで処理能力を増やす考え方。

パーティショニングとの違い

記事では、パーティショニングとシャーディングの違いを分けて説明している。

パーティショニングは、同じデータベースサーバー内でテーブルを分割すること。 大きなテーブルをレンジやハッシュで分け、クエリ対象を絞りやすくする。 ただし、物理的には同じサーバーに載っているため、サーバー全体の性能上限は変わらない。

シャーディングは、分割したデータを複数のデータベースサーバーへ配置すること。 データだけでなく、CPU や I/O の負荷も分散される。 その代わり、どのシャードにクエリを投げるか、シャードをまたぐ JOIN やトランザクションをどう扱うか、障害時にどう復旧するかが難しくなる。

実際には、シャーディングされた各シャード内でさらにパーティショニングすることもある。 たとえばユーザー ID でシャードを分け、各シャード内では日付でパーティションを切る、といった構成。

シャーディングを使うべきケース

シャーディングが向いているのは、単一データベースでは処理しきれないほどデータ量やトラフィックが増えている場合。 ユーザー数、書き込み量、履歴データ、分析対象データが大きくなり、インデックス調整やクエリ改善だけでは追いつかない状況で検討する。

具体的には、次のような兆候がある。

  • ストレージ容量が単一ノードの現実的な上限に近づいている。
  • 書き込み負荷が高く、レプリカ追加だけでは解決しない。
  • ホットなテーブルが大きくなり、インデックスや VACUUM、バックアップ、リストアが重すぎる。
  • 垂直スケールの費用対効果が悪くなっている。
  • 地域、テナント、ユーザーなど、自然な分割軸がある。

一方で、小規模から中規模のシステム、シンプルなワークロード、チームに分散 DB 運用の知識がない状況では、シャーディングは過剰になりやすい。 シャーディングは性能問題を解くが、設計と運用の複雑性を確実に増やす。

キー・ハッシュシャーディング

キーまたはハッシュによるシャーディングでは、ユーザー ID や注文 ID のようなキーをハッシュし、その結果でシャードを決める。 たとえば hash(user_id) % shard_count のようなルールを使う。

この方式の利点は、データが比較的均等に分散しやすいこと。 特定のレンジにアクセスが集中する場合でも、ハッシュにより負荷が散りやすい。

欠点は、シャード数変更が難しいこと。 シャード数を変えると、ハッシュ結果と配置先の対応が変わり、多くのデータを移動する必要がある。 また、範囲検索には向かない。 「ある期間の全注文」のようなクエリは、複数シャードを横断する可能性が高い。

レンジシャーディング

レンジシャーディングでは、日付、数値 ID、地域などの範囲でシャードを分ける。 たとえば user_id 1-1000000 はシャード A、1000001-2000000 はシャード B、という形。

利点は、範囲検索と相性がよいこと。 時系列データなら月ごとや年ごとに分けることで、古いデータのアーカイブやホットデータの分離がしやすくなる。

欠点はホットスポット。 常に最新の日付へ書き込むようなシステムでは、最新レンジのシャードだけに負荷が集中する。 レンジの切り方を誤ると、古いシャードは空いているのに新しいシャードだけが詰まる。

垂直シャーディング

垂直シャーディングは、行ではなく列や機能領域で分ける考え方。 ユーザーの基本情報、認証情報、プロフィール、課金情報、監査ログなどを別々のデータベースに置く。

頻繁にアクセスする小さなデータと、重いが頻度の低いデータを分離できる。 セキュリティ要件が違うデータを分ける用途にも向く。

ただし、元は同じエンティティに見えていた情報が別データベースに分かれるため、JOIN やトランザクションが複雑になる。 アプリケーション側で複数データベースをまたいだ読み書きを扱う必要がある。

ディレクトリベースシャーディング

ディレクトリベースの方式では、どのデータがどのシャードにあるかを管理するルックアップテーブルを用意する。 アプリケーションはまずディレクトリを見て、対象データのシャードを知る。

利点は柔軟性。 特定のテナントだけを別シャードへ移す、ホットなユーザーだけを分離する、といった操作がやりやすい。

欠点は、ディレクトリ自体が重要な依存点になること。 ディレクトリが落ちるとルーティングできない。 また、ディレクトリと実データの整合性を保つ必要がある。

実装方式

シャーディングの実装にはいくつかの層がある。

アプリケーション層で実装する方式は、制御の自由度が高い。 アプリケーションがシャードキーを計算し、接続先を選ぶ。 ただし、全アプリケーションが同じルールを守る必要があり、再シャーディングやマイグレーションの実装負荷が高い。

ミドルウェアを使う方式では、ProxySQL や Vitess のような中間層がルーティングを担う。 アプリケーションからは単一の接続先に見えやすくなるが、ミドルウェアの運用や制約を理解する必要がある。

データベース製品の機能を使う方式もある。 MySQL Cluster、MariaDB の一部機能、分散 SQL データベースなどが該当する。 TiDB のような分散 SQL は、水平スケール、可用性、分散トランザクション、MySQL 互換性をデータベース側で提供し、アプリケーション側のシャーディング実装を減らす方向の選択肢になる。

導入手順

記事では、シャーディング導入を段階的に考えることを勧めている。

最初に、本当にシャーディングが必要かを確認する。 クエリ改善、インデックス、キャッシュ、読み取りレプリカ、アーカイブ、パーティショニングで解決できないかを先に見る。

次に、現在と将来のデータ量、トラフィック、成長率を見積もる。 どのテーブルがボトルネックか、書き込みか読み取りか、どのクエリが重いかを測る。

その上で、シャードキーを選ぶ。 よいシャードキーは、データと負荷を均等に分散し、主要なクエリができるだけ単一シャードで完結する。 キー選択を誤ると、シャードを増やしてもホットスポットや横断クエリが残る。

テスト環境で移行、ルーティング、障害、再配置、バックアップ、リストアを検証する。 本番導入後は、シャードごとの容量、レイテンシ、エラー、ホットスポットを監視し、必要に応じてリバランスする。

TiDB の位置づけ

記事では TiDB を、分散 SQL によって手動シャーディングの複雑性を軽減する選択肢として紹介している。 TiDB は MySQL 互換の SQL インターフェースを持ち、水平スケール、可用性、ACID 分散トランザクション、HTAP、ハイブリッドクラウドやマルチクラウド構成を特徴としている。

手動シャーディングでは、アプリケーションやミドルウェアがシャードルーティングや横断処理を担う。 分散 SQL では、その多くをデータベース側へ寄せる。 もちろん、分散 SQL も運用や性能特性の理解は必要だが、アプリケーションにシャーディングロジックを散らすよりも扱いやすい場合がある。

要点

  • シャーディングは、論理データセットを複数の物理ノードに分散する水平スケール手法。
  • パーティショニングは同一 DB 内の分割、シャーディングは複数 DB サーバーへの分散。
  • ハッシュは均等分散しやすいが再シャーディングと範囲検索が難しい。
  • レンジは範囲検索に強いがホットスポットが起きやすい。
  • ディレクトリ方式は柔軟だが、ルックアップテーブルの可用性と整合性が重要。
  • シャーディングは小規模システムには過剰で、運用複雑性を増やす。
  • TiDB のような分散 SQL は、手動シャーディングの複雑性を DB 側に寄せる選択肢。

タグ

database #sharding #distributed-sql #scalability #tidb