mcpField
D
database-design

database-design

github.com/krzysztofsurdyデータ・データベース3644 回インストール2 日前に更新
リポジトリ

database-design について

アプリケーション開発者向けのデータベース設計スキルです。アクセスパターンや一貫性の要件に基づくスキーマ設計を扱い、データモデリング、インデックス、データベースの種類、移行方法を解説します。

主な機能

  1. 01リレーションのモデル化と正規化
  2. 02ワークロードに応じたDB選定
  3. 03クエリに基づくインデックス設計
  4. 04安全なスキーマ変更の計画
  5. 05パーティショニングとシャーディングの検討

活用シーン

  1. 01新規アプリケーションのスキーマ設計
  2. 02頻出クエリ向けインデックスの改善
  3. 03本番環境のスキーマ変更の計画

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/krzysztofsurdy/code-virtuoso --skill database-design

インストール後、Claude Code で/database-designを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
indexing-strategies.md
migration-patterns.md
modeling-patterns.md

詳細

データベース設計

データベース設計は、データ駆動型アプリケーションの長期的な保守性、パフォーマンス、正確性を左右します。スキーマに関する初期の決定を後から覆すには、大きなコストがかかります。すべてのテーブル、列、インデックス、制約には、アクセスパターンとビジネスルールに基づく理由が必要です。

データモデリングの原則

アクセスパターンから始める

エンティティがドメインモデル上でどのように見えるかではなく、アプリケーションがデータをどのように読み書きするかを中心にテーブルを設計します。すべてのスキーマ設計の判断は、次の2つの問いに基づきます。

  1. 最も頻繁に実行されるクエリは何ですか? これによって、テーブル構造、インデックス、非正規化の方針が決まります。
  2. データにはどのような整合性保証が必要ですか? これによって、正規化のレベル、制約、トランザクション境界が決まります。
エンティティ間のリレーションシップ
リレーションシップ実装方法使用する場面
1対1子テーブルに UNIQUE 制約付きの外部キーを設定ほとんどアクセスされない列を別のテーブルに分割する場合、または必ず1件であることを保証する場合
1対多子テーブルに親を参照する外部キーを設定注文と注文明細、ユーザーと住所
多対多複合主キーを持つ結合テーブルタグと記事、学生とコース
属性付き多対多2つの外部キー以外に独自の列を持つ結合テーブル成績を持つ履修登録、役割を持つメンバーシップ
自己参照同じテーブルを参照する外部キー組織図、カテゴリーツリー、スレッド形式のコメント
命名規則

一貫した命名により、チームやツールをまたいだ混乱を防げます。

  • テーブル:snake_case の複数形の名詞(order_items、user_addresses)
  • 列:値の内容を表す snake_case の単数形(created_at、total_amount)
  • 外部キー:<referenced_table_singular>_id(user_id、order_id)
  • インデックス:idx_<table>_<columns>(idx_orders_user_id_created_at)
  • 制約:chk_<table>_<rule>、uq_<table>_<columns>、fk_<table>_<referenced>

正規化と非正規化

正規形
形式ルール違反の例
1NFすべての列が原子的な値を保持し、繰り返しグループがない1つの列にカンマ区切りのタグを保存する
2NFすべての非キー列が主キー全体に依存する複合キーのテーブルで、キーの一部だけに依存する列がある
3NF非キー列が別の非キー列に依存しない郵便番号から都市が決まるのに、city と zip_code の両方を保存する
BCNFすべての決定項が候補キーである部屋から建物が決まるものの、部屋がキーではないスケジュールテーブル
非正規化する場面

正規化はデータの不整合を防ぎますが、JOIN が増えます。非正規化は、次の場合に限って選択的に行います。

  • 読み取りが多いワークロードが中心で、プロファイリングによって JOIN のコストが測定できる
  • レポート用テーブルで、そうしなければコストの高いクエリが必要になるデータの事前集計が必要
  • 計算値のキャッシュによって、読み取りのたびに再計算する手間を省ける(例:注文行に order_total を保存する)
  • ドキュメント指向のアクセスで、集約全体を1回の読み取りで取得する

安全に非正規化するためのルール:

  1. 正規化された信頼できる唯一の情報源を必ず保持します。非正規化データは派生キャッシュです
  2. 非正規化したコピーをどのように、いつ更新するかを定義します(トリガー、アプリケーションイベント、バッチジョブ)
  3. 情報源とコピーの間に生じるずれを監視します
  4. 非正規化が必要な理由と、それが対応するアクセスパターンを文書化します

データベースの種類の選択

種類強み適している用途
リレーショナル(PostgreSQL、MySQL)ACID トランザクション、複雑なクエリ、成熟したツール、JOINリレーションを持つ構造化データ、トランザクション処理、多くの CRUD アプリケーション
ドキュメント(MongoDB、DynamoDB)柔軟なスキーマ、ネストされたデータ、水平スケーリング集約単位でアクセスするデータ、急速に変化するスキーマ、テナントごとの分離
キー・バリュー(Redis、Memcached)サブミリ秒の読み取り、シンプルなデータモデルセッションストレージ、キャッシュ、カウンター、レート制限
カラムファミリー(Cassandra、ScyllaDB)高い書き込みスループット、ワイド行、線形スケーリング時系列データ、IoT テレメトリ、追記中心のワークロード
グラフ(Neo4j、Neptune)トラバーサルクエリ、リレーション中心のデータソーシャルネットワーク、レコメンデーションエンジン、不正検知
時系列(TimescaleDB、InfluxDB)タイムスタンプ付きデータ向けに最適化、自動パーティショニングメトリクス、モニタリング、金融のティックデータ

ポリグロットパーシステンス(同じシステムの異なる部分に異なるデータベースを使用すること)は、アクセスパターンが異なる場合に妥当です。これは、1つのデータベースを深く学ぶことを避けるための方法ではありません。


インデックスの基礎

インデックスは、書き込みの遅延と追加のストレージを代償に、読み取りを高速化します。すべてのインデックスは、クエリパターンに照らして、その存在意義を示す必要があります。

インデックスの種類
種類構造最適な用途
B-tree平衡木、ソート済みデータ等価検索と範囲検索、ORDER BY、汎用的なインデックスのほとんど
Hashハッシュテーブル完全一致の検索のみ。範囲検索には対応しません
GiST汎用検索木空間データ、幾何クエリ、範囲型、最近傍検索
GIN汎用転置インデックス全文検索、JSONB の包含検索、配列のメンバー検索
BRINブロック範囲インデックス自然な順序(タイムスタンプ、連番 ID)でデータが格納された大規模テーブル
複合インデックスの設計

複合インデックスでは、列の順序が重要です。左端プレフィックスルールにより、(a, b, c) の複合インデックスは、(a)、(a, b)、(a, b, c) で絞り込むクエリには対応しますが、(b, c) のみで絞り込むクエリには対応しません。

列の順序に関するガイドライン:

  1. 等価条件を先に配置します(= で比較する列)
  2. 範囲条件を最後に配置します(>、<、BETWEEN で比較する列)
  3. 等価条件の列の中では、最も選択性の高い列を先に配置します
カバリングインデックスと部分インデックス
  • カバリングインデックス:クエリに必要なすべての列を含むため、データベースはインデックスだけを読み取ります。INCLUDE(PostgreSQL)を使用するか、列をインデックスキーに追加します。
  • 部分インデックス:条件に一致する行のみをインデックス化し、サイズと書き込みのオーバーヘッドを削減します。大規模なテーブルの小さなサブセットをクエリする場合(例:WHERE status = 'pending')に最適です。

インデックスの種類、EXPLAIN の分析、アンチパターンについて詳しくは、インデックス戦略リファレンスを参照してください。


スキーマの進化

スキーマの変更は避けられません。重要なのは、それによって稼働中のアプリケーションが壊れるかどうかです。

後方互換性のある変更(安全)
  • 新しい NULL 許容列を追加する
  • 新しいテーブルを追加する
  • 新しいインデックスを追加する(一部のエンジンでは短時間ロックされる場合があります)
  • 列の型の幅を広げる(例:VARCHAR(50) から VARCHAR(100))
互換性を損なう変更(移行戦略が必要)
  • 列の名前を変更する、または削除する
  • 互換性のない方法で列の型を変更する
  • NULL 値を含む既存の列に NOT NULL 制約を追加する
  • テーブルを分割または統合する
エクスパンド・コントラクトパターン

本番環境でダウンタイムなしに互換性を損なう変更を行う場合:

  1. 拡張: 古い構造と並行して新しい構造を追加します
  2. 移行: 古い構造から新しい構造にデータをバックフィルし、移行中は両方に書き込みます
  3. 切り替え: 新しい構造を使用するようにアプリケーションコードを更新します
  4. 縮小: 参照するものがなくなったら、古い構造を削除します

ダウンタイムなしの戦略、ロールバック手法、複数のツールを使った例について詳しくは、移行パターンリファレンスを参照してください。


パーティショニングとシャーディング

テーブルのパーティショニング(単一データベース)
戦略仕組みユースケース
範囲値の範囲(例:月ごと)で行を分割します時系列データ、ログテーブル、アーカイブ
リスト個別の値(例:地域ごと)で行を分割しますマルチテナントデータ、地理的なセグメント化
ハッシュ列のハッシュ値に基づいて行を分散します自然な範囲が存在しない場合の均等な分散
シャーディング(複数のデータベース)

シャーディングでは、データを個別のデータベースインスタンスに分散します。単一インスタンスの最適化(インデックス作成、キャッシュ、読み取りレプリカ)を使い切った後にのみ使用してください。

シャードキーの選定基準:

  • カーディナリティが高い:均等に分散できるよう、多くの異なる値がある
  • ほとんどのクエリに含まれている:すべてのシャードに対する散布・集約を回避できる
  • 安定している:作成後に値が変更されない
  • ホットスポットがない:書き込みが集中する値(例:現在の日付)でシャーディングしない

クイックリファレンス:よくある設計上の誤り

誤り影響対策
外部キー制約がない孤立した行、データの不整合そうしない理由が文書化されていない限り、常に外部キーを定義します
インデックスの過剰な作成書き込みの遅延、ストレージの無駄実際のクエリで WHERE、JOIN、ORDER BY に使用される列のみインデックスを作成します
更新戦略なしで計算値を保存するデータの陳腐化、検出されにくいバグ非正規化とあわせて、更新トリガー、イベント、またはバッチジョブを定義します
変更される値に ENUM 型を使用する値が追加されるたびにスキーマを移行する必要がある代わりに外部キーを持つルックアップテーブルを使用します
金額を浮動小数点数として保存する丸め誤差DECIMAL/NUMERIC を使用するか、整数のセント単位で保存します
created_at / updated_at タイムスタンプがない監査ができず、デバッグが困難になる既定で、すべてのテーブルにタイムスタンプ列を追加します
あらゆる場所で汎用的な type + type_id のポリモーフィズムを使用する参照整合性がなく、クエリが複雑になるまず STI、CTI、または個別のテーブルを検討します

参考ファイル

参考資料内容
モデリングパターンポリモーフィック関連(STI/CTI/TPT)、論理削除、監査履歴、時間データ、自己参照ツリー、JSON 列
インデックス戦略B-tree/hash/GiST/GIN の詳細、複合インデックスの設計、カバリングインデックスと部分インデックス、EXPLAIN 分析、アンチパターン
マイグレーションパターンバージョンベースと状態ベースのマイグレーション、拡張・縮小、データマイグレーション、ロールバック戦略、複数ツールの例

他のスキルとの連携

状況推奨スキル
クエリパフォーマンスとキャッシュの最適化performance
ドメインモデルと集約の設計clean-architecture
データベースを利用するリソースを公開する API の構築api-design
データベースとのやり取りのテストtesting

コメント