データ設計が重要な理由
データ設計は一度決めると直せない。その理由を 5 つの事実で示す。
なぜデータ設計だけは「後から直す」が通用しないのか
アプリケーションのコードは、壊れたら書き直せば済みます。画面レイアウトや UI のリファクタリングは、最悪ユーザーに影響せず翌日にはリリースできます。しかしデータ設計(テーブル構造・型・キー・正規化・制約)だけは、一度本番投入するとやり直しが極端に難しい領域です。
- 既に保存されたデータをどうするかという移行問題が常について回る(バックフィル・ダブルライト・段階的切替)
- 依存するクエリ・アプリ・BI レポート・外部連携がすべて同時に書き換わる必要がある
- 巨大テーブルの ALTER は数時間〜数日のロックになり、停止時間を伴う
だからこそ、最初の設計に投じる時間は、後から払う移行コストに比べて桁違いに安いというのがデータ設計の鉄則です。本ページでは、この鉄則を支える 5 つの事実と、「最も重要」と断言するかの文脈依存性を整理します。
事実 1: データは資産そのもの — 失うと戻らない
開発の世界では「コードは書き直せるが、失われたデータは戻らない」という非対称性があります。ユーザー情報・取引履歴・コンテンツ・ログ — これらこそがサービスの本質的な価値であり、アプリはその価値にアクセスするための「窓口」に過ぎません。
- ログイン機能が落ちても 30 分で復旧できるが、ユーザーテーブルが吹き飛んだら顧客資産そのものが消失する
- 決済バグは返金で挽回できるが、会計データの改ざんや欠損は監査で致命傷になる
- 推薦モデルは再学習できるが、学習元の行動ログが無ければ再現不可能
さらに、業務で扱うデータはそれを作ったアプリケーションより遥かに長く生き残ります。
- 会計・取引・人事データは法定保存期間(7〜10 年)があり、アプリがリプレイスされても消せない
- ユーザーアカウントや取引履歴は、フレームワーク(Rails / Django / Spring 等)を何度乗り換えても受け継がれる
- 基幹系では20〜30 年前の COBOL や Oracle の DB がそのまま現役、ということが普通に起こる
一方でアプリのフレームワークや言語、フロントエンドは数年単位で入れ替わります。つまり、
アプリの寿命 << データの寿命
この非対称性があるため、データ構造は「今のアプリに合うか」ではなく「10 年後も使える意味を持つか」で設計する必要があります。特定フレームワークの都合で列名を決めたり、今使っている ORM の制約で型を曲げたりすると、フレームワークを捨てるときに設計ごと捨てる羽目になります。
事実 2: 修正コストは時間とともに指数的に増える
同じ「users.name を first_name と last_name に分ける」修正でも、タイミングによってコストは桁違いに変わります。
| タイミング | データ量 | 必要な作業 | 体感コスト |
|---|---|---|---|
| 設計段階(ER 図を書いている最中) | 0 行 | 図と DDL を書き換えるだけ | 数分 |
| 開発初期(ステージングのみ) | 〜数百行 | マイグレーション追加+コード修正+DB 再構築 | 数時間 |
| 本番リリース直後 | 数万〜数十万行 | 上記+既存データの機械分割(姓名判別ロジック)+UI 修正+API 互換レイヤ | 数日〜数週間 |
| 運用 3 年後 | 数百万〜数千万行 | 上記+BI レポート全部+外部連携先の調整+オンライン移行+ロック対策 | 数ヶ月〜プロジェクト化 |
「設計段階で気づいていれば 5 分」が、「運用 3 年目だと 3 ヶ月の移行プロジェクト」になる、という曲線を描きます。しかも後者では分割不可能なレガシーデータ(姓名一体で登録されたまま)が永遠に残るケースも珍しくありません。
この非線形性を知っているかどうかが、設計時に「面倒だから後回し」と「今しっかり決める」を分ける判断基準になります。
事実 3: 整合性の最終砦は DB — アプリは信用できない
「NOT NULL や外部キーは ORM / バリデーションで担保しているから DB 側は緩くていい」という設計は、長期的にはほぼ確実に破綻します。理由はシンプルで、データベースに書き込む経路はアプリだけではないからです。
- 本番障害対応で DBA が
psql/sqlplusから直接 UPDATE する - データ移行スクリプト(Python / シェル)が一括 INSERT する
- BI チームが分析用に別テーブルへ ETL する
- 次の世代のアプリが別の ORM / 別のバリデーションルールで同じテーブルに書く
- バグったバッチジョブが想定外の値を書き込む
アプリ側のチェックは「今日のこのアプリを通った場合」しか守れません。DB に NOT NULL / CHECK / FOREIGN KEY / UNIQUE が宣言されていれば、どの経路から書き込まれても整合性が強制される。これは代替できない DB だけの機能です。
詳しくは 制約(CONSTRAINT) と 外部キー設計 を参照してください。
事実 4: 性能は設計段階でほぼ決まる
「リリース後に遅くなったらインデックスを足せばいい」と思いがちですが、設計ミスはインデックスでは救えない領域があります。
- 正規化不足: JSON や CSV 文字列に複数値を詰めてしまうと、条件検索で全件スキャンを避けられない
- 主キー設計のミス: UUID v4 を主キーにすると B-Tree の挿入位置がランダムになり、数千万行規模で挿入性能が数倍劣化する(UUID インデックス)
- 外部キー/インデックス前提の JOIN: 後から大きなテーブルに複合インデックスを足そうとすると、数時間のロックや再構築が必要になる
- 型の誤り: 数値を
VARCHARで持ったり、日時を文字列で持ったりすると、比較・範囲検索でインデックスが使われない(暗黙の型キャスト)
つまり性能の 70〜80% は、インデックスやクエリチューニングではなく、最初のテーブル設計で決まっています。設計時に押さえるべき観点は インデックス設計入門 と 高速化チェックリスト にまとめています。
事実 5: スケーラビリティの限界を決めるのは DB
アプリサーバは比較的「横に並べる」ことで簡単にスケールできます。コンテナを増やし、ロードバランサに追加すればリクエスト処理能力はほぼ線形に伸びます。一方 DB のスケールは設計・運用ともに一気に難しくなる のが現実です。
| 観点 | アプリサーバ | DB |
|---|---|---|
| スケール手段 | インスタンス追加(水平) | リードレプリカ / シャーディング / パーティション |
| 状態 | 基本ステートレス | 状態そのものを持つ(データの整合性が絡む) |
| 書き込みのスケール | 気にしなくてよい | 書き込みは原則 1 マスター — 分割設計が必須 |
| 運用難易度 | オートスケールで大半は自動化 | レプリ遅延・フェイルオーバ・シャード再分割など人が介在 |
| 切り戻し | 古いバージョンに戻す | スキーマ移行後の切り戻しは極めて困難 |
結果として、DB はサービス成長の「天井」になりがちです。アプリ側が捌けていても、DB が限界に達すればそのサービスは成長を止めます。設計段階で次のような観点を押さえておくと、天井を遠ざけられます。
- 読み書き比率を見積もる(読み 9 割ならレプリカで拡張しやすい/書き中心ならシャード分割を早めに検討)
- シャーディングキーを主キーに織り込めるか(後からの付け替えは事実上移行プロジェクト)
- 集計・分析用途は分離(リードレプリカ / データウェアハウス / CQRS)
- ホットスポットを避ける(連番 PK の末尾集中、時系列のラストパーティション集中)
具体的なテクニックは インデックス設計入門 と 高速化チェックリスト を参照してください。
補足: 「最も重要」かどうかは文脈次第
ここまで「データ設計は最重要」という立場で解説してきましたが、これを絶対視すると見誤ります。実際にはシステムの性質によってボトルネックの主役は変わります。
| システムの性質 | 主なボトルネック | データ設計の位置づけ |
|---|---|---|
| 業務システム / SaaS / EC | DB(クエリ・スキーマ・整合性) | 最重要 |
| 金融 / 会計 | DB(整合性・監査ログ) | 最重要 |
| ゲームサーバ(リアルタイム対戦) | ネットワーク遅延・インメモリ状態 | 重要だが主役ではない(永続化部分に限定) |
| AI 推論・機械学習基盤 | GPU / モデルサイズ / 推論レイテンシ | 学習データのパイプラインに限って重要 |
| IoT / ストリーミング | スループット・メッセージブローカ | 書き込みスケール設計としては重要 |
| 静的コンテンツ配信 / CDN | キャッシュ・配信網 | 管理用 DB のみ、比重は小さい |
本サイト Query Go が扱う RDB は、上表でいう業務システム / SaaS / EC / 金融系の中核技術です。扱っているシステムがこの領域なら「データ設計が最重要」は正しいと考えて差し支えありません。一方、リアルタイムゲームや AI 推論など別領域では、他のレイヤと並列で検討する必要があります。
設計段階で最低限決めるべき項目
では、どの項目を設計段階で確定しておけばよいのでしょうか。後から変更コストが大きい順に並べると以下のようになります。
| 項目 | 設計で決めること | 後回しの危険度 |
|---|---|---|
| エンティティとテーブル分割 | 何を 1 テーブルにし、何を別テーブルにするか(正規化) | 極大(全クエリが書き直し) |
| 主キーの選び方 | 連番整数 / UUID v7 / 自然キー のどれにするか | 大(FK 含めて再設計) |
| 列の型 | 数値 / 文字列 / 日時 / 金額(NUMERIC)/ JSON の選択 | 大(既存データ変換が必要) |
| 外部キーと制約 | FK / UNIQUE / CHECK / NOT NULL | 中(違反データが貯まってから足せない) |
| NULL 可否 | NULL 許容の意味はあるか、デフォルト値はどうするか | 中(意味論が変わる) |
| 監査列 | created_at / updated_at / deleted_at を全テーブルに入れるか | 中(後から全テーブル ALTER) |
| インデックス | 主な検索条件と ORDER BY に合う複合インデックス | 小〜中(後から追加可能、ただしロック注意) |
上 3 つ(テーブル分割・主キー・型)は特に「後から直す」が事実上できない領域なので、設計レビューで必ず合意を取っておきます。
よくある初期設計のアンチパターン
設計時に避けたい典型例を挙げます。いずれも初期は楽に見えて、1〜3 年後に大きな移行コストに化けるものです。
- なんでも文字列(VARCHAR): 数値も日時も真偽も全部
VARCHAR。比較も範囲検索もインデックスも機能しなくなる - 1 列に CSV や JSON を詰め込む:
tags = "赤,青,緑"のようにすると正規表現検索しかできず、絞り込みが全件スキャンになる - 主キーに業務キーを使う: メールアドレスやユーザー ID 文字列を主キーにすると、変更したい時に全テーブルの FK を張り替える羽目になる
- 外部キーを張らない: 「アプリで担保するから」と FK を省くと、孤児レコードが年単位で積もって清掃プロジェクト化する
- 金額を FLOAT / DOUBLE にする: 0.1 + 0.2 ≠ 0.3 になり、会計監査で数円単位のズレを追うことになる
- 日時を文字列で持つ: タイムゾーン情報が消え、国をまたぐサービスでダブル計上・未計上が起きる
- soft delete を途中から導入: 全クエリに
WHERE deleted_at IS NULLを付け回るメンテコストが永続化する。初期に決めておく
次に読む
データ設計の重要性を押さえたら、次は具体的な設計手法に進みます。
- RDBMS の種類 — どの RDBMS を選ぶかで得意な設計パターンが変わる
- データ型の基本 — 型選択は設計の基礎
- 正規化 1NF〜3NF — テーブル分割の原則
- ER 図入門 — 設計を可視化し共有する
- インデックス設計入門 — 設計段階で性能を確保する
- 高速化チェックリスト — 性能問題が出たときに立ち返る一覧
