非正規化
読み取り性能を向上させるために、意図的にデータの冗長性を持たせる設計手法
データベース設計
非正規化とは
非正規化 (Denormalization) は、正規化されたデータベースに意図的に冗長性を持たせ、読み取り性能を向上させる設計手法である。JOIN を排除し、1 回のクエリで必要なデータを取得できるようにする。
正規化 vs 非正規化
正規化と非正規化の違いを以下にまとめる。
| 観点 | 正規化 | 非正規化 |
|---|---|---|
| 冗長性 | なし | あり (意図的) |
| 読み取り | 遅い (JOIN が必要) | 速い (1 回のクエリ) |
| 書き込み | 速い (1 箇所を更新) | 遅い (複数箇所を更新) |
| 一貫性 | 高い | 低い (同期が必要) |
| ストレージ | 効率的 | 冗長 |
DynamoDB での非正規化
DynamoDB は JOIN をサポートしないため、非正規化が基本設計となる。
// ❌ 正規化 (RDS 的な設計): 2 回のクエリが必要
// orders テーブル: { orderId: "1", userId: "U1" }
// users テーブル: { userId: "U1", name: "Alice" }
// ✅ 非正規化: 1 回のクエリで取得
{
"orderId": "1",
"userId": "U1",
"userName": "Alice",
"items": [
{ "product": "りんご", "price": 100 }
]
}
非正規化のパターン
非正規化のパターンを以下にまとめる。
| パターン | 説明 | 例 |
|---|---|---|
| 埋め込み | 関連データをアイテム内に埋め込む | 注文に顧客名を含める |
| 複製 | 同じデータを複数のアイテムに持つ | 商品名を注文明細にも持つ |
| 集計値の事前計算 | 集計結果を保存 | 注文の合計金額を保存 |
一貫性の維持
非正規化したデータの一貫性を維持する方法:
// DynamoDB トランザクションで複数アイテムを同時更新
await db.transactWrite({
TransactItems: [
{ Update: { TableName: 'orders', Key: { id: orderId }, UpdateExpression: 'SET userName = :name', ... } },
{ Update: { TableName: 'users', Key: { id: userId }, UpdateExpression: 'SET #name = :name', ... } },
],
});
// DynamoDB Streams で非同期に同期
[users テーブル更新] → [DynamoDB Streams] → [Lambda] → [orders テーブルの userName を更新]
いつ非正規化するか
冗長に持つ判断は、読み取りで浮く分が更新時に複数箇所を直す手間を上回るかで決まる。
| 場面 | 判断 | 理由 |
|---|---|---|
| DynamoDB にデータを置く | 非正規化を前提にする | JOIN が使えないため、正規化したままでは 1 画面のために複数回のクエリを重ねることになる |
| 読み取りが書き込みより圧倒的に多い | 非正規化する | 遅くなるのは頻度の低い書き込み側だけで、多数を占める読み取りは 1 回のクエリで済むようになる |
| 書き込みが多く、値の食い違いが許されない | 正規化のまま (RDS) | 複製した値を持つと更新のたびに全複製をトランザクションで揃える必要があり、書き込みが多いほどその負担が積み上がる |
| 埋め込んだ値がほとんど変わらない | 非正規化する | 顧客名のように更新が稀な項目なら、同期のために動く経路が走る回数自体が少なく済む |
非正規化の背景や設計思想は関連書籍に詳しい。
この記事は役に立ちましたか?
関連用語
データ正規化
データベースの冗長性を排除し、データの一貫性を保つためにテーブル構造を分割 / 整理する手法
DynamoDB
AWS のフルマネージド NoSQL データベースで、ミリ秒単位のレイテンシとシームレスなスケーリングを提供する
データベースインデックス
検索クエリの高速化のためにデータベースが維持する補助的なデータ構造
正規表現
文字列のパターンマッチングと置換を行うためのミニ言語
Database
データを整理して保存 / 検索 / 更新するための仕組み。システムの根幹を担う
CQRS
データの読み取り (Query) と書き込み (Command) を別々のモデルで処理し、それぞれを独立して最適化するアーキテクチャパターン