SDN
← 概念一覧へ
データベース

非正規化(デノーマライゼーション)

Denormalization

あえてデータの冗長なコピーを複数テーブルに持たせ、重いJOINを避けて読み取りを高速化する手法。読み取りが圧倒的に多いシステムで、書き込み性能を犠牲に読み取りを稼ぐ。

正規化 vs 非正規化の read・write 性能

正規化は write が速く read が遅い(JOIN が必要)。非正規化はデータを重複させるため read が速く write が遅い、という逆のトレードオフです。縦軸は相対速度(高いほど速い・例示)。

read 速度write 速度

※ 値は絶対値ではなく「傾向を示す例示」です。相対的な大小関係を読み取るためのものです。

キーポイント
  • 冗長なコピーを複数テーブルに持たせ、高コストなJOINを回避する。
  • 読み取り性能を、書き込み性能の犠牲のもとに改善する。
  • 前提: 読み:書き = 100:1 〜 1000:1 のような読み取り過多のシステム。
  • materialized views(PostgreSQL / Oracle)が支援機能。
  • 短所: データが重複するため整合性維持が難しく設計が複雑化。書き込みが多いと、正規化版より遅くなることすらある。
トレードオフ

JOINを減らせるので読み取りは速くなるが、同じデータを複数箇所にコピーするため『更新時に全コピーを直す』必要があり、書き込みが重く・整合性維持が難しくなる。読み取りが圧倒的に多い(100:1以上)前提で初めて割に合う。書き込みが多いシステムで安易に使うと、むしろ正規化版より遅くなる。

まず『正規化』とは(前提)

正規化とは、データの重複をなくす設計です。例えばユーザー名は『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 を回避できる(代わりに名前変更時は全コピーの更新が必要)。

図を描画中...

関連する概念

この概念で腕試し

関連する 5 問のクイズに挑戦できます。

クイズを解く