# ロストアップデートと書き込みスキュー
## 定義
**更新のロスト(lost update)**とは、複数のトランザクションが read-modify-write サイクル(値を読み、変更を計算し、書き戻す)を並行に行ったとき、後の書き込みが前の変更を含まないまま値を上書きし、片方の変更が失われる現象である。カウンタの増分、JSON ドキュメントへの要素追加、Wiki ページの同時編集などで発生する。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 8 Transactions]] "Preventing Lost Updates")
**書き込みスキュー(write skew)**とは、2 つのトランザクションが同じオブジェクト集合を読み、それぞれ異なるオブジェクトを更新することで、読み取り時点の前提(premise)がもはや成り立たない不変条件違反を引き起こす現象である。更新のロストの一般化と言え、同一オブジェクトを更新する特殊ケースがダーティライトや更新のロストになる。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 8 Transactions]] "Write Skew and Phantoms", "Characterizing write skew")
**ファントム(phantom)**とは、あるトランザクションの書き込みが別トランザクションの検索クエリの結果を変えてしまう現象である。書き込みスキューの多くの例(会議室予約・ユーザー名の一意性・二重支出防止)は、検索条件に一致する行の**不在**をチェックしてから行を**追加**するパターンで発生し、`SELECT FOR UPDATE` で対象行にロックを付けようにも対象行自体が存在しないため無力化される。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 8 Transactions]] "Phantoms causing write skew")
## 更新のロストを防ぐ手法
- **原子的書き込み操作**: `UPDATE counters SET value = value + 1` のような DB 組み込みのアトミック更新。read-modify-write サイクルを回避できる最善の選択肢
- **明示的ロック**: `SELECT ... FOR UPDATE` でアプリケーション側が対象行を明示的にロックする
- **自動検出**: PostgreSQL の repeatable read、Oracle の serializable、SQL Server のスナップショット分離は更新のロストを自動検出し中断させる(MySQL/InnoDB の repeatable read はこれを行わない)
- **条件付き書き込み(CAS)**: `UPDATE ... WHERE content = 'old content'` のように、読んだ時点の値と現在値が一致する場合のみ書き込む
- **複製環境での競合解決**: マルチリーダー/リーダーレスレプリケーションではロックや CAS が使えないため、CRDT のような可換操作か、アプリケーションレベルでの兄弟バージョン(sibling)のマージが必要になる。LWW(last write wins)は更新のロストを起こしやすい
(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 8 Transactions]] "Atomic write operations", "Explicit locking", "Automatically detecting lost updates", "Conditional writes (compare-and-set)", "Conflict resolution and replication")
## 書き込みスキューを防ぐ手法(選択肢は限定的)
- 単一オブジェクトの原子操作は複数オブジェクトが絡むため無効
- スナップショット分離下の自動検出も書き込みスキューは対象外(PostgreSQL repeatable read・MySQL/InnoDB repeatable read・Oracle serializable・SQL Server スナップショット分離いずれも検出しない)
- 複数オブジェクトにまたがる制約(例: 「オンコール医師は最低 1 人」)は多くの DB がネイティブサポートしない
- 対象行を明示的にロック(`SELECT FOR UPDATE`)する。ただしファントムの場合は対象行がまだ存在しないため無効
- **マテリアライズドコンフリクト**: 会議室×時間帯のような組み合わせを事前にすべて行として用意し、ロック対象を人工的に作る。ただし実装が難しくアプリケーションデータモデルへの漏れ出しが醜いため最終手段
- 最も確実な解決策は**直列化可能な分離レベル**を使うこと
(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 8 Transactions]] "Characterizing write skew", "Materializing conflicts")
## 横断的知見
- **2つの独立した教科書が、ドメインの異なる具体例(オンコール医師 対 銀行口座)で同型のライトスキューパターンを示している**: DDIA の代表例は「オンコール医師は最低1人」という制約下で2人の医師が同時に非番申請するケースだが、詳説 データベース5章§5.3.3は口座A1(100$)・A2(150$)の合計が0以上でなければならないという制約下で、T1がA1から200$、T2がA2から200$を引く(FEKETE04 由来)という金融ドメインの具体例を用いる。両者とも「(a) 複数オブジェクトにまたがる不変条件が存在し、(b) 各トランザクションは読み取り時点でその不変条件が満たされていることを確認してから独立したオブジェクトを更新し、(c) 個々には要件を満たすと判断されるが組み合わせると不変条件に違反する」という完全に同一の抽象構造を持つ。ドメインの異なる2つの具体例が同じ抽象パターンに帰着することは、ライトスキューが特定業務ロジックの偶発的なバグではなく、MVCC ベースの分離レベル一般に内在する構造的な問題であることを裏付ける。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 8 Transactions]] "Characterizing write skew", [[@2021__OReillyJapan__詳説 データベース - Chapter 5 トランザクション処理とリカバリ]] §5.3.3)
- **読み取り/書き込みアノマリーの分類語彙(ダーティリード・ノンリピータブルリード・ファントムリード・ロストアップデート・ダーティライト・ライトスキュー)は、2つの教科書が独立に同一の6分類へ収束している**: 詳説 データベース5章§5.3.3はSQL標準(MELTON06)を出典に読み取りアノマリー3種・書き込みアノマリー3種を定義し、DDIA も同じ6用語を用いる。特にダーティライト(「トランザクションの1つがコミットされていない値を受け取ってそれを変更してから保存した状況」)という、実務ではあまり注目されない用語まで両者が独立に採用している点は、この分類が業界標準として確立していることを示す。(Source: [[@2021__OReillyJapan__詳説 データベース - Chapter 5 トランザクション処理とリカバリ]] §5.3.3, [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 8 Transactions]] "Write Skew and Phantoms")
## 未解決の問い
- マテリアライズドコンフリクトのような「ロック対象の人工的な作成」は、他ドメイン(例: 分散スケジューリング・リソース予約システム)でも同型のパターンとして現れるか。
- 書き込みスキューを実運用で検知するための静的解析・テスト手法は、本章執筆時点(2026 年)でどこまで実用化が進んだか。原文は「研究レベルに留まる」と述べるが、直列化可能性(→ [[直列化可能性]])側の実装進化がこの必要性自体を減らしているか。
- CRDT による可換操作(→ 第6章レプリケーション領域)は、書き込みスキューが起きる「不変条件チェック→書き込み」パターンにも適用できるか、それとも根本的に相性が悪いか。
- 詳説 データベースの口座残高の例と DDIA のオンコール医師の例が同型であることが確認できた(本節参照)。この抽象パターンを一般化した「不変条件テンプレート」を整理すれば、ライトスキューが起こりうるスキーマ設計を静的に検出できるか。
## 関連
- ソース: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 8 Transactions]] / [[@2021__OReillyJapan__詳説 データベース - Chapter 5 トランザクション処理とリカバリ]]
- 概念: [[ACIDと分離レベル]] / [[スナップショット分離とMVCC]] / [[直列化可能性]]
## 出典
- [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 8 Transactions]]("Preventing Lost Updates", "Write Skew and Phantoms", "Serializability")
- [[@2021__OReillyJapan__詳説 データベース - Chapter 5 トランザクション処理とリカバリ]](§5.3.3 読み取り/書き込みアノマリーの定義と口座残高の具体例)