非正規化

読み取り性能を向上させるために、意図的にデータの冗長性を持たせる設計手法

データベース設計

非正規化とは

非正規化 (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 (NoSQL) ✅ 基本的に非正規化
読み取りが圧倒的に多い ✅ 非正規化
書き込みが多く一貫性が重要 ❌ 正規化 (RDS)
データの更新頻度が低い ✅ 非正規化 (同期コストが低い)

非正規化の背景や設計思想は関連書籍に詳しい。

関連用語