database-sharding について
データベースの水平スケーリングに向けたシャーディング戦略、ルーターテンプレート、実装ガイドを提供するスキルです。シャード構成を設計・運用する開発者向けに、キー選定、複数シャード操作、再バランスも扱います。
主な機能
- 01ハッシュ・範囲・ディレクトリ方式
- 02シャードルーターテンプレート
- 03シャードキー選定基準
- 04複数シャードのクエリとトランザクション指針
- 05監視と再バランスのガイド
活用シーン
- 01ユーザーやテナントデータのキー選定
- 02データベースのシャードルーティング層の構築
- 03シャード追加とデータ移行の計画
- 04ホットスポットや複数シャードクエリの調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill database-shardingインストール後、Claude Code で/database-shardingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/cross-shard-queries.mderror-catalog.mdimplementation-patterns.mdrebalancing-guide.mdshard-key-selection.mdsharding-strategies.mdtemplates/cross-shard-aggregation.tsdirectory-router.tshash-router.tsrange-router.ts詳細
database-sharding
ハッシュ、範囲、ディレクトリベースの戦略を用いた水平スケーリングのための、包括的なデータベースシャーディングパターンです。
クイックスタート(10 分)
ステップ 1:テンプレートからシャーディング戦略を選択します:
# Hash-based (even distribution)
cat templates/hash-router.ts
# Range-based (time-series data)
cat templates/range-router.ts
# Directory-based (multi-tenancy)
cat templates/directory-router.ts
ステップ 2:シャードキーの条件を選択します:
- ✅ 高いカーディナリティ(数百万の一意な値)
- ✅ 均等な分布(単一の値が 5% を超えない)
- ✅ 不変(変更されない)
- ✅ クエリとの整合性(WHERE 句の 80% 以上で使用される)
ステップ 3:ルーターを実装します:
import { HashRouter } from './hash-router';
const router = new HashRouter([
{ id: 'shard_0', connection: { host: 'db0.example.com' } },
{ id: 'shard_1', connection: { host: 'db1.example.com' } },
{ id: 'shard_2', connection: { host: 'db2.example.com' } },
{ id: 'shard_3', connection: { host: 'db3.example.com' } },
]);
// Query single shard
const user = await router.query('user_123', 'SELECT * FROM users WHERE id = $1', ['user_123']);
重要なルール
✓ 必ず行うこと
| ルール | 理由 |
|---|---|
| クエリにシャードキーを含める | すべてのシャードのスキャンを回避します(100 倍遅くなります) |
| シャードの分布を監視する | 障害が発生する前にホットスポットを検出します |
| 事前にリバランスを計画する | 後からシャードを簡単に追加することはできません |
| 不変のシャードキーを選択する | キーの変更はデータ移行の大きな問題につながります |
| 本番データで分布をテストする | 合成データでは実際のホットスポットが隠れてしまいます |
| データ局所性のために非正規化する | 関連データを同じシャードに保持します |
✗ 絶対に行わないこと
| アンチパターン | 問題点 |
|---|---|
| 範囲シャーディングで連番 ID を使用する | 最新のシャードに書き込みが集中します(ホットスポット) |
| タイムスタンプをシャードキーにする | 最近のシャードに負荷が集中します |
| 2PC を使わずにシャード間トランザクションを行う | データの破損や不整合が発生します |
| 一貫性ハッシュを使わずに単純な剰余演算を使う | 全データを再シャーディングせずにシャードを追加できません |
| NULL を許容するシャードキーを使う | NULL の特別な処理によってホットスポットが発生します |
| シャードのルーティングレイヤーを設けない | シャードがハードコードされ、リバランスできません |
重大なエラー 7 件
エラー 1:誤ったシャードキーの選択(ホットスポット)
症状:1 つのシャードにトラフィックの 80% 以上が集中します 修正:
// ❌ Bad: Low cardinality (status field)
shard_key = order.status; // 90% are 'pending' → shard_0 overloaded
// ✅ Good: High cardinality (user_id)
shard_key = order.user_id; // Millions of users, even distribution
エラー 2:クエリにシャードキーが含まれていない
症状:クエリがすべてのシャードをスキャンします(非常に遅くなります) 修正:
// ❌ Bad: No shard key
SELECT * FROM orders WHERE status = 'shipped'; // Scans all 100 shards!
// ✅ Good: Include shard key
SELECT * FROM orders WHERE user_id = ? AND status = 'shipped'; // Targets 1 shard
エラー 3:範囲シャーディングで連番 ID を使用している
症状:最新のシャードにすべての書き込みが集中します 修正:
// ❌ Bad: Range sharding with auto-increment
// Shard 0: 1-1M, Shard 1: 1M-2M, Shard 2: 2M+ → All new writes to Shard 2!
// ✅ Good: Hash-based sharding
const shardId = hash(id) % shardCount; // Even distribution
エラー 4:リバランス戦略がない
症状:初期のシャード数のまま拡張できません 修正:
// ❌ Bad: Simple modulo
const shardId = hash(key) % shardCount; // Adding 5th shard breaks ALL keys
// ✅ Good: Consistent hashing
const ring = new ConsistentHashRing(shards);
const shardId = ring.getNode(key); // Only ~25% of keys move when adding shard
エラー 5:シャード間トランザクション
症状:データの不整合や書き込みの一部欠落が発生します 修正:
// ❌ Bad: Cross-shard transaction (will corrupt)
BEGIN;
UPDATE shard_1.accounts SET balance = balance - 100 WHERE id = 'A';
UPDATE shard_2.accounts SET balance = balance + 100 WHERE id = 'B';
COMMIT; // If shard_2 fails, shard_1 already committed!
// ✅ Good: Two-Phase Commit or Saga pattern
const txn = new TwoPhaseCommitTransaction();
txn.addOperation(shard_1, 'UPDATE accounts SET balance = balance - 100 WHERE id = ?', ['A']);
txn.addOperation(shard_2, 'UPDATE accounts SET balance = balance + 100 WHERE id = ?', ['B']);
await txn.execute(); // Atomic across shards
エラー 6:変更可能なシャードキー
症状:レコードが別のシャードに移動し、重複が発生します 修正:
// ❌ Bad: Shard by country (user relocates)
shard_key = user.country; // User moves US → CA, now in different shard!
// ✅ Good: Shard by immutable user_id
shard_key = user.id; // Never changes
エラー 7:監視がない
症状:ホットスポットに気づかないまま、突然パフォーマンスが低下します 修正:
// ✅ Required metrics
- Per-shard record counts (should be within 20%)
- Query distribution (no shard > 40% of queries)
- Storage per shard (alert at 80%)
- Latency p99 per shard
詳細な修正方法を含む 10 件すべてのエラーについては、references/error-catalog.md を読み込んでください。
シャーディング戦略
| 戦略 | 最適な用途 | メリット | デメリット |
|---|---|---|---|
| ハッシュ | ユーザーデータ、均等な負荷が重要な場合 | ホットスポットがなく、予測可能です | 範囲クエリが複数のシャードに分散します |
| 範囲 | 時系列データ、ログ、追記専用データ | 範囲クエリが効率的で、アーカイブにも適しています | 最近のシャードにホットスポットが発生します |
| ディレクトリ | マルチテナント、複雑なルーティング | 柔軟で、リバランスが容易です | ルックアップのオーバーヘッド、単一障害点 |
本番環境での事例(Instagram、Discord、Salesforce)を含む詳細な比較については、references/sharding-strategies.md を読み込んでください。
シャードキーの選択条件
| 条件 | 重要度 | 確認方法 |
|---|---|---|
| 高いカーディナリティ | 重大 | COUNT(DISTINCT shard_key) > shard_count × 100 |
| 均等な分布 | 重大 | どの値も全体の 5% を超えない |
| 不変 | 重大 | 値が変更されない |
| クエリとの整合性 | 高 | クエリの 80% 以上に含まれる |
| データ局所性 | 中 | 関連レコードが同じ場所にまとまる |
判断ツリー:
- ユーザー中心のアプリ →
user_id - マルチテナント SaaS →
tenant_id - 時系列データ/ログ →
timestamp(範囲シャーディング) - 製品カタログ →
product_id
包括的な判断ツリーとテスト戦略については、references/shard-key-selection.md を読み込んでください。
設定の概要
ハッシュベースのルーター
import { HashRouter } from './templates/hash-router';
const router = new HashRouter([
{ id: 'shard_0', connection: { /* PostgreSQL config */ } },
{ id: 'shard_1', connection: { /* PostgreSQL config */ } },
]);
// Automatically routes to correct shard
const user = await router.query('user_123', 'SELECT * FROM users WHERE id = $1', ['user_123']);
範囲ベースのルーター
import { RangeRouter } from './templates/range-router';
const router = new RangeRouter(shardConfigs, [
{ start: Date.parse('2024-01-01'), end: Date.parse('2024-04-01'), shardId: 'shard_q1' },
{ start: Date.parse('2024-04-01'), end: Date.parse('2024-07-01'), shardId: 'shard_q2' },
{ start: Date.parse('2024-07-01'), end: Infinity, shardId: 'shard_q3' },
]);
// Range queries target specific shards
const janEvents = await router.queryRange(
Date.parse('2024-01-01'),
Date.parse('2024-02-01'),
'SELECT * FROM events WHERE created_at BETWEEN $1 AND $2'
);
ディレクトリベースのルーター
import { DirectoryRouter } from './templates/directory-router';
const router = new DirectoryRouter(directoryDBConfig, shardConfigs);
// Assign tenant to specific shard
await router.assignShard('tenant_acme', 'shard_enterprise');
// Route automatically
const users = await router.query('tenant_acme', 'SELECT * FROM users');
リファレンスを読み込むタイミング
戦略の選択
ロード references/sharding-strategies.md:
- ハッシュ、範囲、ディレクトリのいずれかを選ぶ場合
- 本番環境での例(Instagram、Discord)が必要な場合
- ハイブリッド方式を計画する場合
シャードキーの選択
ロード references/shard-key-selection.md:
- 新しいプロジェクトのシャードキーを選ぶ場合
- 既存のシャードキーを評価する場合
- 本番データで分散をテストする場合
実装
ロード references/implementation-patterns.md:
- シャードルーターを一から構築する場合
- コンシステントハッシュを実装する場合
- トランザクション処理(2PC、Saga)が必要な場合
- モニタリング/メトリクスを設定する場合
クロスシャード操作
ロード references/cross-shard-queries.md:
- シャード間で集計(COUNT、SUM、AVG)する必要がある場合
- クロスシャード結合を実装する場合
- シャード間のページネーションを構築する場合
- Scatter-Gather パターンを最適化する場合
リバランシング
ロード references/rebalancing-guide.md:
- 新しいシャードを追加する場合
- シャード間でデータを移行する場合
- ゼロダウンタイム移行を計画する場合
- 不均等な負荷を調整する場合
エラーの防止
ロード references/error-catalog.md:
- パフォーマンスの問題をトラブルシューティングする場合
- シャードアーキテクチャをレビューする場合
- 修正方法が記載された、文書化済みの 10 件すべてのエラーを確認する場合
セットアップ完了チェックリスト
シャーディングの前:
- 本番データでシャードキーの分散をテスト済み
- クエリの 80% 以上にシャードキーが含まれている
- モニタリング基盤の準備が完了している
- リバランシング戦略を計画済み
ルーターの実装:
- シャードルーティングレイヤー(シャードをハードコードしない)
- シャードごとのコネクションプーリング
- エラー処理と再試行
- メトリクスの収集(シャードごとのクエリ数、レイテンシ)
シャードの設定:
- 初期シャード数は 4~8(拡張の余地を確保)
- コンシステントハッシュまたは仮想シャード
- シャードごとのレプリカ(HA)
- シャードごとのバックアップ戦略
アプリケーションの変更:
- すべてのクエリにシャードキーを含める
- クロスシャード結合を排除(非正規化)
- トランザクション境界を遵守
- コネクションプーリングを設定
本番環境の例
変更前(単一データベースに過負荷が発生):
// Single PostgreSQL instance
const db = new Pool({ host: 'db.example.com' });
// All 10M users on one server
const users = await db.query('SELECT * FROM users WHERE status = $1', ['active']);
// Query time: 5000ms (slow!)
// DB CPU: 95%
// Disk: 500GB, growing
変更後(8 台のサーバーにシャーディング):
// Hash-based sharding with 8 shards
const router = new HashRouter([
{ id: 'shard_0', connection: { host: 'db0.example.com' } },
{ id: 'shard_1', connection: { host: 'db1.example.com' } },
// ... 6 more shards
]);
// Query single user (targets 1 shard)
const user = await router.query('user_123', 'SELECT * FROM users WHERE id = $1', ['user_123']);
// Query time: 10ms (500x faster!)
// Query all shards (scatter-gather)
const allActive = await router.queryAll('SELECT * FROM users WHERE status = $1', ['active']);
// Query time: 800ms (parallelized across 8 shards, 6x faster than single)
// Result: Each shard handles ~1.25M users
// DB CPU per shard: 20%
// Disk per shard: 65GB
// Can scale to 16 shards easily (consistent hashing)
既知の問題の防止
文書化済みの 10 件すべてのエラーを防止します:
- ✅ 誤ったシャードキー(ホットスポット)→ まず分散をテスト
- ✅ クエリにシャードキーがない → コードレビュー、リンティング
- ✅ クロスシャードトランザクション → 2PC または Saga パターンを使用
- ✅ 連番 ID によるホットスポット → ハッシュベースのシャーディングを使用
- ✅ リバランシング戦略がない → 初日からコンシステントハッシュを使用
- ✅ タイムスタンプシャーディングによるホットスポット → ハッシュと範囲のハイブリッド方式を使用
- ✅ 変更可能なシャードキー → 不変キー(user_id)を選択
- ✅ ルーティングレイヤーがない → 最初からルーターで抽象化
- ✅ モニタリングがない → シャードごとのメトリクスを追跡
- ✅ 弱いハッシュ関数 → MD5、MurmurHash3、xxHash を使用
参照:詳しい修正方法は references/error-catalog.md
リソース
テンプレート:
templates/hash-router.ts- ハッシュベースのシャーディングtemplates/range-router.ts- 範囲ベースのシャーディングtemplates/directory-router.ts- ディレクトリベースのシャーディングtemplates/cross-shard-aggregation.ts- 集計パターン
リファレンス:
references/sharding-strategies.md- 戦略の比較references/shard-key-selection.md- キー選択ガイドreferences/implementation-patterns.md- ルーターの実装references/cross-shard-queries.md- クエリパターンreferences/rebalancing-guide.md- 移行戦略references/error-catalog.md- 文書化済みの 10 件すべてのエラー
本番環境の例:
- Instagram:メディア向けの範囲シャーディング
- Discord:メッセージ向けのハッシュシャーディング
- Salesforce:組織向けのディレクトリシャーディング
本番環境で検証済み | 10 件のエラーを防止 | MIT License