第7章:「トランザクション = 安心」のウラにある、すごく雑で不安な現実
第7章:「トランザクション = 安心」のウラにある、すごく雑で不安な現実
『データ指向アプリケーションデザイン(DDIA)』第7章を読んだメモです
トランザクション=万能、と思ってたけど全然そんなことなかった
そのギャップと現実の“設計責任”について自分なりに噛み砕きました
ACIDって魔法の言葉みたいに語られがちだけど…
「トランザクションはACIDを保証してるから安心」って話、
一見頼もしそうだけど、ちゃんと読んでいくと:
- A(Atomicity)→ ロールバックできる単位だけ
- C(Consistency)→ DBじゃなくアプリ設計の責任
- I(Isolation)→ 実装依存で全然意味が変わる
- D(Durability)→ 書いた瞬間から消えない…のか?
それぞれの「実際の中身」が曖昧なことに気づく
図7: 単一ノード・ローカルトランザクションのライフサイクル
一貫性(C)は“文脈依存”。だから設計で担保しないと破綻する
「データが一貫している状態」とは何か?
これはDBが勝手に判断できるものじゃない
- 残高がマイナスじゃない
- 在庫数より注文数が多くない
- 入力と合計が合ってる
といった業務ロジックのルールがないと、整合なんて語れない
ACIDの「C」は設計者の仕事そのもの
分離性(I)は「難しすぎて覚えてられない」やつランキング上位
- Read Committed:読んだデータはもうコミット済み
- Snapshot Isolation:トランザクション開始時点のスナップショットを見る
- Serializable:すべての実行が順次であるかのように見える
- Repeatable Read…ってどれだっけ?
トランザクション分離レベルは、用語が似てて混乱しがち
でも実際の処理に直結する、すごく実践的な論点
PostgreSQLはMVCCで“見える世界”を分けてくれてる
PostgreSQLはマルチバージョン同時実行制御(MVCC)を採用してて:
- Insert/Update/Deleteのたびに新しいバージョンを追加
- 古いバージョンは“見えなく”する
- vacuumでようやく物理削除
「同じデータを別のトランザクションが見てる」状況でも破綻しにくい
世代管理で分離性を“擬似的に”実現してるとも言える
トランザクション分離レベルで許される or 許されない世界
たとえば:
- ダーティーリード(未確定な値を読み取る)
- 更新ロスト(2つの変更がぶつかって片方が消える)
- ファントムリード(同じクエリなのに行が増える)
- 書き込みスキュー(条件満たしてたのに、実行結果が壊れる)
このあたり、実はアプリケーションで対処しないとヤバいケースも多い
「トランザクションに任せておけば安心」では済まされない
アトミック書き込み、実は“書き込む前にチェック”でしかない
あるトランザクションが、対象レコードに対して:
- 他のやつ書いてないよね? をチェック
- OKだったら書く
- ダメならリトライ or 中断
この「チェックしてから書く」流れは分散排他制御の最小単位
設計者側でバッティング条件をちゃんと設計しないと発火する
“結果として問題ない”というPostgreSQLの哲学
PostgreSQLはSerializable Isolationでもリトライによる整合性担保をする仕組みがあって:
- 古いスナップショットをもとに処理した
- 書き込みトランザクションにぶつかった
- リトライして、整合性を保った形に書き直す
という「あとから帳尻合わせ」が可能
でも、これを過信すると大量リトライでパフォーマンス死ぬ
スナップショット分離が安全とは限らない理由
スナップショット分離では、トランザクション開始時点の世界しか見えない
- 読み取った後に他の誰かが書き込んだ
- それに気づかず別の更新をした
- 整合性が壊れる
読んで→考えて→書くまでの一連の流れが分離されすぎると、**スキュー(歪み)**が生まれる
“トランザクション設計”とは、性能と信頼性のトレードオフ
- 分離レベルを上げると競合が増える(整合性は強くなる)
- 下げるとパフォーマンスは上がる(整合性は“現場任せ”)
このトレードオフをどこに落とすかが肝
しかもその判断は「今のユースケース」と「将来の使い方」を見越して決めなきゃいけない
✍️ まとめ:トランザクションは“すごく便利な不完全性”の上に成り立ってる
トランザクションって、「全部任せれば安心」な仕組みに見えて、
実際は「設計者が意思を持って制御する仕組み」だった
- どのくらい信用するか
- どこまで任せて、どこからはアプリで担保するか
- その結果、どんなパフォーマンスになるか
この線引きこそが“トランザクション設計”の本質
整合性も耐障害性も、「どこまで諦めるか」で実現されてる
💡 設計Tips
- トランザクションに任せきりにしない(業務ロジック次第で破綻する)
- 分離レベルの選定はユースケース依存
- MVCCの挙動を前提にすると PostgreSQL は柔軟に扱える
- スナップショット分離でもスキューに注意
- リトライ前提設計はパフォーマンスとの相談が必要
Discussion