イミュータブルデータモデルと webアプリケーションにおける現実解

これは第2のドワンゴ Advent Calendar 2017の5日目です
5日11時時点で2日担当の yonex がまだ記事書いてないですが、気にせず続けます。niconico(く)のリリースが来年と聞いて残念な気持ちです。

おめー誰よ?

ドワンゴ Advent Calendar皆勤賞っぽいですが、私はドワンゴ社員ではありません。
定年をとうに過ぎたおじさんです。

前置き

web アプリケーションの開発において、データモデリングはとても重要です。
SIerではDBAとか言って専門の設計担当がいるみたいですが、中小webサービス企業でそこまでの分業ができるわけもなく、大体においてwebアプリケーション(サーバサイド)エンジニアが担当することになります。

イミュータブルデータモデル

詳細はリンクに譲りますが、「履歴を全て残すようなデータ設計にし、 UPDATE を廃することで情報の追跡可能性を確保、堅牢な設計にする」モデリング手法です。
原則この手法に従うと、そうそう汚いモデルにはならないという優れもの(雑)

イミュータブルデータモデルの欠点

イミュータブルデータモデルを適用すると、イベント(状態変更のトリガー)を別テーブルに置く、ということになります。それは「状態が他のテーブルに依存する」ことを意味します。
状態を取得したい場合には、対象のテーブルに、関連イベントのテーブルを全部joinして最新イベントを取得して…って手順を踏むこととなり、リアルタイム性が重要視される web アプリケーションではパフォーマンス面での問題が発生します。

状態のほか、口座に対する入金イベントと出金イベントがあるような場合だと、残高を取得する際に同様のパフォーマンスの問題が起きます。

欠点への対応方法

  1. DB の性能をあげて頑張る
  2. あるタイミングでの状態を含むスナップショットを保存して、状態取得の際に必要なイベントの量を減らす
  3. 「キャッシュとしての状態カラム」を作り、イベントの発生タイミングで状態カラムを書き換え、状態の取得についてはそのカラムを参照する

DB の性能をあげて頑張る

「札束で殴る」パターン。これで大丈夫なら大丈夫ですが、だいたいイベントデータが増えると破綻します。

スナップショットを保存して、状態取得の際に必要なイベントの量を減らす

スナップショット用のテーブルを用意し、特定時点での状態(を含んだ各種データ)を保存します。
取得データ量の減少と信頼性の担保の両立ができますが
* ロジックが複雑になる(スナップショットをいつ作成するかを考える必要がある)
* イベントの量によってはスナップショットのデータ量がバカにならない

といった欠点もあります。
スナップショットの存在を要件としてポジティブにできれば採用できそうです1

「キャッシュとしての状態カラム」を作り、状態の取得についてはそのカラムを参照する

状態取得のコストをミニマムにできます。
書き込みと読み込みを比べて極端に読み込みが多い場合2に有効です

キャッシュとしてのカラムが存在するということは、
* 冗長である(イベントレコードの作成とキャッシュカラムの変更を同一トランザクションで行う必要が出てくる)
* 読み込みはキャッシュ不使用に比べて速い
* (厳密なイミュータブルデータモデルではなくなる)

ということになります。
厳密な設計に限定的な妥協をして、書き込みの手間を増やして読み込みを効率化します。

「限定的な妥協」の限定がなくなってしまうと、ただの崩れた設計になってしまうので、
ドキュメント等で「基本はイミュータブルデータモデルで、このカラムはキャッシュである」という認識を共有するのが大事です

まとめ

堅牢な設計は大事だけど、パフォーマンスも大事。要求に応じて適切な設計を選びましょう。


  1. 残高の話の場合は採用しやすそうな気がします 

  2. だいたいの web サービスはそうなると思います