非正規化(デノーマライゼーション)
Denormalization
あえてデータの冗長なコピーを複数テーブルに持たせ、重いJOINを避けて読み取りを高速化する手法。読み取りが圧倒的に多いシステムで、書き込み性能を犠牲に読み取りを稼ぐ。
まず『正規化』とは(前提)
正規化とは、データの重複をなくす設計です。例えばユーザー名は『usersテーブル』に1か所だけ持ち、注文テーブルからはユーザーIDで参照する。こうすると名前変更時に1か所直せば済み、整合性が保ちやすい。RDBMSの教科書的な正しい設計です。
しかし、注文一覧にユーザー名を表示するたびに、**毎回JOIN(結合)**が必要になります。JOINは重い処理。大量に走ると遅くなります。
非正規化のアイデア
そこであえてユーザー名を注文テーブルにもコピーして持っておく。すると注文一覧の表示でJOINが不要になり、読み取りが速くなります。これが非正規化です。
図を描画中...
なぜ『読み取りが多いシステム』限定なのか
コピーを持つと、元データが変わったとき全コピーを更新しないといけません。ユーザーが名前を変えたら、注文テーブルのコピーも全部直す。これは書き込み時の負担増。
だから割に合うのは『読み取りが書き込みより圧倒的に多い(100:1〜1000:1)』場合だけ。読むたびにJOINを節約できる回数が、書くときの余分な手間を大きく上回るからです。逆に書き込みが多いと、コピー更新の負担でかえって遅くなることすらあります。
materialized views という支援
PostgreSQLやOracleには materialized view(実体化ビュー) という機能があり、JOINの結果をあらかじめ計算して保存しておけます。手動でコピーを管理する代わりに、DBに非正規化的なキャッシュを作らせるイメージです。
つまずきポイント
- 非正規化は『正規化のサボり』ではなく、読み取り最適化のための意図的な設計判断。むやみにやると整合性地獄になる。
- 整合性の維持(全コピーを同期して更新する)が設計上の最大の課題。更新漏れがあると、場所によって違う名前が表示される、といったバグになる。
- 書き込みが多いシステムでは逆効果になりうることを忘れない。
📊 図解
図解: 正規化テーブル vs 非正規化テーブル
正規化版は ORDERS が user_id だけを持ち、表示のたびに USERS と JOIN が必要。非正規化版(ORDERS_DENORM)は user_name のコピーを持つので JOIN を回避できる(代わりに名前変更時は全コピーの更新が必要)。
図を描画中...