1287 文字
6 分
シャーディングとは
データベースを複数のシャードと呼ばれる小さなデータベースに分割する技術
シャードはそれぞれ独立したサーバーでホストされ、データは複数のシャードに分散される
利点
- スケーラビリティ: データ量が増加しても、シャードを追加することで簡単にスケールアウトできる
- パフォーマンス: データが複数のサーバーに分散されるため、クエリ処理のパフォーマンスが向上する
- 可用性: 一つのシャードが故障しても、他のシャードが存続するため、可用性を向上できる
種類
キーベースシャーディング
データのキーに基づいてシャードに割り当てる
Python
# キーベースシャーディングの例
def get_shard_id(key): # ハッシュ関数を使用してキーに基づいてシャードIDを計算 return hash(key) % num_shards
# データベースへのアクセスdef get_data(key): shard_id = get_shard_id(key) shard = shards[shard_id] # シャードからデータを取得 return shard.get(key)
# データベースへの書き込みdef set_data(key, value): shard_id = get_shard_id(key) shard = shards[shard_id] # シャードにデータを設定 shard.set(key, value)
# シャードのリストshards = [ # シャード1 ..., # シャード2 ..., # シャードN ...,]
# 使用例key = "user_123"value = {"name": "John Doe", "age": 30}
get_data(key)set_data(key, value)レンジベースシャーディング
データの値の範囲に基づいてシャードに割り当てる
Python
# レンジベースシャーディングの例
def get_shard_id(value): # 値の範囲に基づいてシャードIDを計算 if value < 1000: return 0 elif value < 10000: return 1 else: return 2
# データベースへのアクセスdef get_data(value): shard_id = get_shard_id(value) shard = shards[shard_id] # シャードからデータを取得 return shard.get(value)
# データベースへの書き込みdef set_data(key, value): shard_id = get_shard_id(value) shard = shards[shard_id] # シャードにデータを設定 shard.set(key, value)
# シャードのリストshards = [ # シャード1 (値 < 1000) ..., # シャード2 (1000 <= 値 < 10000) ..., # シャード3 (値 >= 10000) ...,]
# 使用例key = "product_123"value = 5000
get_data(value)set_data(key, value)ディレクトリベースシャーディング
データの属性に基づいてシャードに割り当てる
Python
# ディレクトリベースシャーディングの例
def get_shard_id(country): # 国名に基づいてシャードIDを計算 if country == "日本": return 0 elif country == "アメリカ": return 1 else: return 2
# データベースへのアクセスdef get_data(key, country): shard_id = get_shard_id(country) shard = shards[shard_id] # シャードからデータを取得 return shard.get(key)
# データベースへの書き込みdef set_data(key, value, country): shard_id = get_shard_id(country) shard = shards[shard_id] # シャードにデータを設定 shard.set(key, value)
# シャードのリストshards = [ # シャード1 (日本) ..., # シャード2 (アメリカ) ..., # シャード3 (その他) ...,]
# 使用例key = "user_123"value = {"name": "John Doe", "age": 30}country = "日本"
get_data(key, country)set_data(key, value, country)課題
- 複雑性: シャーディングの設計と運用は複雑な場合が多い
- データの整合性: 複数のシャードに分散されたデータの整合性を保つ必要がある
- トランザクション: シャードにまたがるトランザクション処理は複雑な場合が多い
例
- 1億人の顧客情報を持つデータベースをシャーディングする
- 顧客IDに基づいて100個のシャードに分割する
- 各シャードは1000万人の顧客情報を格納する
- クエリ処理は、顧客IDに基づいて特定のシャードに送信される
- 商品情報を扱うECサイトのデータベースをシャーディングする
- 商品カテゴリに基づいてシャードに分割する
- 例えば、家電、衣類、食品などのカテゴリごとにシャードを作成する
- ユーザーは、特定のカテゴリの商品のみを検索する場合、そのカテゴリのシャードにのみクエリを送信する
関連記事
補足:実運用で気をつけたいこと
シャーディングを実際に入れるときに躓きやすいポイントを、追記としてまとめておきます。
シャードキー選びの重み
シャードキーは「あとから簡単に変えられない」設計判断です。次の観点で慎重に選びます。
- 分布が偏らないか:例えば「都道府県」をキーにすると、東京シャードだけ突出してホットスポットになりやすい。
- アクセスがそのキーに沿うか:ほとんどのクエリが
WHERE user_id = ?で始まるなら、user_id をキーにするとシャードに閉じた問い合わせで済む。 - マルチテナントなら tenant_id:テナントごとに完結する処理が多いSaaSでは、tenant_id がほぼ最適解になりがち。
クロスシャード操作の難しさ
シャードをまたいだ JOIN、集計、トランザクションは、単一DBの世界で当たり前にできていたことが急に難しくなります。
- JOIN:基本は「アプリケーション側でデータを集めて結合する」設計に倒す。
- 集計:各シャードに集計を投げて、結果を上で合算する。
- トランザクション:分散トランザクション(2PCなど)は重い。Sagaパターンで結果整合に倒すのが現代的。
パーティショニングとの違い
混同されやすいので並べて整理します。
- パーティショニング:同じサーバー内で、テーブルを論理的に分割する。MySQL の
PARTITION BY RANGEなどが該当。 - シャーディング:複数の物理サーバーにまたがってテーブル(あるいはデータベース)を分割する。
パーティショニングはストレージや管理性のための仕掛け、シャーディングはスケールアウトのための仕掛け、と覚えると整理が楽です。
いつ採用すべきか
シャーディングは強力ですが、複雑性のコストも大きいです。「単一DB+レプリカ+キャッシュ」で捌けるなら、まずそちらで頑張るのがセオリー。スケールアップ・読み取りレプリカ・キャッシュを試した上で、書き込みが本気で詰まり始めたタイミングで検討する、くらいの距離感が現実的です。