/note/tech

AIとDB設計を考えてみた(後編) : トランザクションの整合性はDynamoDBに任せたい

要約:

■ 1. マスタ管理の拡張性

  • 疑問の提起:
    • シングルテーブル設計はスケールが容易なDynamoDBなどのNoSQLで定番な方法でRDBであるPostgreSQLで耐えられるのだろうか
    • マスタが巨大化すると詰むのではという疑問
  • 結論:
    • 特に問題はない
  • 理由:
    • RDBはスケールしないと言っても数十万件くらいなら余裕で捌ける
    • 前編で説明した全マスタをまとめて取得するのはどこかで限界が来るけど取得時にMetadataによるフィルタリングをすれば適切に絞り込みが可能
    • 運用が続いて規模が大きくなったら無理が出るのは仕方がない問題が出たときにリファクタすれば良い
    • そもそも肥大化するデータはもはやマスタとして扱うのは正しくなくトランザクションとして扱うべき
    • 大抵なんとかなるしなんとかならなかったらトランザクションに寄せればいいよね

■ 2. 普通のリレーションの問題点

  • 通常のアプローチ:
    • トランザクション用のテーブルを作る
    • マスタのcodeを外部キーに持たせる
    • JOINしてトランザクションと紐づくマスタをセットで取得する
  • 問題点:
    • マスタがトランザクションに拘束されることになる
    • 前編で実現した柔軟なマスタの変更が封印されてしまう
    • 考え直す必要がある

■ 3. トランザクションをマスタから自律させる

  • 問題の本質:
    • マスタが変更されると古いマスタ情報が消えてしまうのが問題
  • 解決策:
    • トランザクションの中に必要なマスタの情報をコピーしてまとめて保存する
    • この単純で分かりやすい力技はとても効果的
    • マスタがどれだけ破壊的に変更や削除されてもトランザクションデータは当時の姿のままで生き残り続ける
  • 手法の名称:
    • こういった手法は一般的にスナップショット・パターンと呼ばれている

■ 4. DynamoDBの採用理由

  • PostgreSQLでの限界:
    • PoCなどの大きくスケールしないケースであればトランザクションもマスタと同じようにJSONBでPostgreSQLの1つのテーブルに保存しても問題ないかもしれない
    • 仮にもトランザクションなので一般的なアプリであれば使っているうちにデータは増え続ける
    • しかもマスタのデータをトランザクションにコピーするのでデータ量も増える
    • スケールできるように考えるべき
  • DynamoDB採用の利点:
    • スケーラビリティ: トランザクションデータは増え続けPostgreSQLでも数十万件程度なら問題ないが数百万件や数千万件となるとシンプルに扱うことが困難でDynamoDBなら自動的にスケールするためこの心配がない
    • 書き込みパフォーマンス: トランザクションは頻繁に書き込まれDynamoDBであればキーバリューストアとして高速な書き込みに最適化されている
    • アクセスパターンが明確: トランザクションは通常特定のキーで検索されDynamoDBはアクセスパターンが事前に定義できる場合に最高のパフォーマンスを発揮しマスタのような自由な検索は不要
  • 使い分け:
    • 検索性が必要なマスタ管理ではPostgreSQLを採用
    • スケーラビリティが重要なトランザクションではDynamoDBがよさそう

■ 5. ルーズなSQLと厳格なNoSQL

  • PostgreSQLでのマスタ:
    • スキーマレスなJSONBで何でも受け入れる
    • 構造の変更が自由
    • 整合性チェックはアプリ側に任せる
    • ルーズで柔軟
  • DynamoDBでのトランザクション:
    • スナップショット・パターンで当時の状態を厳格に保存
    • 一度書き込んだら変更しないでイミュータブル
    • データの整合性は書き込み時点で確定
    • 厳格で不変
  • 設計思想:
    • マスタは変わるものだから変更に強い柔軟な設計にする
    • トランザクションは変わらないものだから厳格に保存して整合性を保つ
    • 一見しただけだとRDBとNoSQLに求めるものが逆転しているように見えるかもしれないがデータの性質に合わせてDBの使い方を考えればこの設計も自然に見えてくる

■ 6. 正規化についての考察

  • 正規化を避けてきた理由:
    • 正規化や非正規化に囚われずにゼロベースで考えデータをありのままに扱いたかった為
  • 正規化の目的の歴史:
    • 正規化理論が確立された50年前の状況を振り返る
    • ストレージが高価だったためコストの観点からデータの冗長性排除が必須だった
    • 更新処理が重かったため更新回数を最小限にする設計が必須だった
    • トランザクション処理が複雑だったため更新箇所を1箇所に集約する必要があった
    • つまり正規化の目的はデータの冗長性排除や不整合の防止というよりも限られたリソースの中で更新処理を効率的に行うことにあった
  • 現代の環境:
    • ストレージコストが劇的に低下したためコスト観点では冗長性はある程度許容できる
    • 読み取り速度が物理的に向上したためJOINによる複雑な結合よりも冗長でもシンプルな構造の方が速い
    • クラウドを活用すればスケールアウトも容易になったため正規化による更新箇所の集約よりもスケーラビリティで対応できる
    • ビジネスのスピードが加速したため厳密性よりも柔軟性が重要
  • 制約の変化:
    • 2026年現在のDB設計は50年前と比べて制約の性質が根本的に変わった
    • 正規化が前提としていた1970年代の制約から解放され新たな制約である開発スピードが支配的になった現代ではデータの性質に応じて正規化しない選択肢も合理的
  • 重要な考え方:
    • 新規システム開発では正規化しないほうがいいという単純な話ではない
    • RDBだから正規化でもモダンだから正規しないでもなくデータの性質や規模や更新頻度や一貫性の要件や変更速度の要求やチームの状況を考えたとき何が最適かを考えることがモダンな設計の本質

■ 7. まとめ

  • エクセルでマスタ管理をできる程度のアプリのDB設計のポイント:
    • マスタ管理はPostgreSQLとシングルテーブルとJSONBで小規模で変更が多いデータで検索の柔軟性が必要でスキーマレスで構造の変更に強い
    • トランザクションはDynamoDBとスナップショット・パターンで大規模で書き込みが多いデータでアクセスパターンが明確でマスタのデータを含めることで整合性を保証
  • 設計の特徴:
    • DBが原因でアプリの拡張性が損なわれるということを避けるDB設計をGeminiくんに考えてもらった
    • その結果マスタは柔軟に作る為にPostgreSQLを使いトランザクションは整合性を確保する為にDynamoDBを使うという直感とは異なる考え方でありながらマスタはPostgresqlでトランザクションはDynamoDBというとても普通なDB使い分けというちょっと面白い設計になった