トランザクションと分離レベル——残高の時系列で考えるACID
複数の更新を一つの仕事として扱うトランザクションを学び、ACIDの四つの保証、読み取りの異常、直列化可能性を架空の残高表で区別します。
- コミットとロールバックを、複数の更新をまとめる境界として説明できる
- ACIDの四つの性質を区別し、業務上の不変条件と対応づけられる
- 架空の時系列からダーティリード、非再現読み取り、ファントムを判別できる
- 分離レベルの一般的な保証と製品固有の実装の違いを区別できる
- 同時進行の結果を直列実行と比較して、直列化可能性の意味を説明できる
📑 目次
1.二つの更新を一つの仕事として考える
口座Aから口座Bへの振替では、「Aを減らす」と「Bを増やす」が組になっています。片方だけを確定すると、振替の目的を満たせません。トランザクションは、このように関連する読み取りや更新を一つの論理的な仕事にまとめる単位です。表の構造やSQLの書き方ではなく、仕事の途中・完了・同時進行をどう区別するかが本節の主題です。
| 境界 | 意味 | 注意点 |
|---|---|---|
| 開始 | 関連する操作を一つの仕事として扱い始める | 開始しただけで業務ロジックが正しくなるわけではない |
| コミット | 仕事が成功したとして変更を確定する | 他の処理がいつその変更を見られるかは分離の規則にもよる |
| ロールバック/中止 | その仕事の未確定の変更を取り消す | 通常、既にコミットした仕事を後からなかったことにはできない |
2.ACIDは四つの異なる問い
| 性質 | 問うこと | 振替での意味 |
|---|---|---|
| Atomicity:原子性 | 一部だけが確定していないか | 引落しと加算を両方確定するか、両方取り消す |
| Consistency:整合性 | 定めた不変条件を守るか | 正しい振替によって総額と残高の条件を保つ |
| Isolation:分離性 | 同時進行の仕事が互いにどう見えるか | 許された読み取り・更新の関係を守る |
| Durability:永続性 | 確定した結果が失われないか | 想定する障害から復旧した後にも確定済み振替を残す |
整合性の基準となる不変条件は、データの意味に応じて定めます。本節では「AとBの合計額は振替前後で同じ」「残高は負にならない」を採用します。データベースは宣言された制約などを検査できますが、誤った振替額や受取先を自動で正しい業務判断に変えてはくれません。各仕事の設計が不変条件を保ち、それを同時進行時にも壊さない分離の仕組みが必要です。
A、Bは振替前、A′、B′は振替後の残高、xは振替額です。すべて円で測ります。ここではA ≥ 0、B ≥ 0、0 ≤ x ≤ Aとし、丸め誤差はありません。合計が保たれるのは同じ額xを減らして足し、外部との出入りがないからです。実際に手数料が発生する台帳なら、手数料の行き先も含めて別の不変条件を設計します。
初期残高をA = 10,000円、B = 5,000円とする。Tという一つの仕事で3,000円をAからBへ振り替える。T内部でAを7,000円にした後、Bへ加える前に中止した。原子性が保たれるなら、取消し完了後の確定残高はどうなるか。最後まで成功した場合とも比べよう。
解答・解説
中止時はTの引落しも取り消され、確定残高はA = 10,000円、B = 5,000円に戻る。成功時はA = 7,000円、B = 8,000円となる。どちらも合計15,000円。「7,000円と5,000円のまま確定」は合計12,000円で、片側だけの確定になり原子性を満たさない。T内部に一時的な途中状態があることと、その状態を確定させることは別である。
3.同時進行では「何をいつ読むか」が変わる
仕事Tと仕事Rが重なって進むとき、Rはどの版の残高を見るのでしょうか。分離性はこの関係を扱います。最も強い基準の一つである直列化可能性(serializability)は、同時進行した仕事の読み取りや最終状態が、仕事を一つずつ順番に行ったどれかの直列実行と等価であることです。内部で本当に一つずつ動かす必要はありません。弱い分離レベルでは、性能などとの兼ね合いで一部の現象が許されます。
| 読み取りで起こる現象 | 紙上での定義 | 架空の短い例 |
|---|---|---|
| ダーティリード | 他の仕事がまだ確定していない変更を読む | RがTの未確定のA = 7,000円を読む。後でTが中止すると、その値は確定しない |
| 非再現読み取り | 同じ仕事で同じ行を読み直すと、他の仕事の確定済み変更により値が変わる | RがA = 10,000円を読み、Tの確定後に同じAを読むと7,000円 |
| ファントム | 同じ条件で検索し直すと、他の仕事の確定済み変更で該当する行の集合が変わる | 残高8,000円以上の口座を検索した後、条件に合う新しい口座が確定追加され、再検索で増える |
ファントムは新規行の追加だけでなく、削除や条件に関わる更新でも起こり得ます。「同じ行の値が変わる」ことと「条件に合う行の集まりが変わる」ことを区別しましょう。一方、これら三つを避けるだけで、あらゆる読み書きの組合せが直列化可能になるとは限りません。
4.四つの分離レベルを保証の下限として読む
| 分離レベル | ダーティリード | 非再現読み取り | ファントム |
|---|---|---|---|
| Read Uncommitted | 許容され得る | 許容され得る | 許容され得る |
| Read Committed | 禁止 | 許容され得る | 許容され得る |
| Repeatable Read | 禁止 | 禁止 | 許容され得る |
| Serializable | 禁止 | 禁止 | 禁止 |
表はSQLの分離レベルを伝統的な三現象で整理した一般的な下限です。「許容され得る」は「必ず起こる」ではありません。製品がより強い保証を提供することもあります。Serializableには、表の三現象を禁止するだけでなく直列実行との等価性という基準があります。製品名やバージョン、読み取りの種類を指定せずに、レベル名だけからすべての挙動を断言しないでください。
再びA = 10,000円、B = 5,000円とする。読み取り専用の仕事Rが二回の別々の読み取りで合計を求める。各読み取りは、その時点の最新の確定済み残高を読むという紙上モデルに従う。Rが先にBを読み、次にTがAからBへの3,000円の振替を確定し、その後RがAを読んだ。Rの計算した合計と、直列実行で得る合計を求めよう。
解答・解説
RはBの古い確定値5,000円とAの新しい確定値7,000円を足し、12,000円と計算する。しかし振替前は10,000 + 5,000 = 15,000円、振替後は7,000 + 8,000 = 15,000円。R→TでもT→RでもRの合計は15,000円なので、この読み取り結果はどちらの直列順序とも等価ではない。未確定値は読んでおらず、ダーティリードではない。異なる時点の値を混ぜた読み取りの不整合である。
| 順序 | 仕事R | 仕事T | 確定済みの全体状態 |
|---|---|---|---|
| t0 | 開始 | 未開始 | A = 10,000円、B = 5,000円 |
| t1 | Bの5,000円を記憶 | 未開始 | A = 10,000円、B = 5,000円 |
| t2 | 待っている | 3,000円の振替を完了してコミット | A = 7,000円、B = 8,000円 |
| t3 | Aの7,000円を読み、合計12,000円と計算して終了 | 終了済み | A = 7,000円、B = 8,000円 |
5.スナップショットと直列化可能性は同じではない
読み取り専用のRが仕事全体を通じて同じ時点のスナップショット(データの版)を読むなら、例題2ではAとBの両方が振替前、または両方が振替後になり、合計は15,000円です。ただし「同じ版を読み続ける」だけで、複数の仕事が条件を判断して別々の行を書き換える場合まで直列化可能になるわけではありません。判断の前提と更新の組合せにも注意が必要です。
別の架空の台帳で残高を10,000円とする。仕事Pは現在の残高に1,000円を加える。仕事Qは現在の残高の10%を加える。各仕事は読み取りと更新を一まとまりに行う。この数値では端数が出ない。P→QとQ→Pの二つの直列実行で最終残高を計算しよう。
解答・解説
P→Qなら(10,000 + 1,000) × 1.1 = 12,100円。Q→Pなら10,000 × 1.1 + 1,000 = 12,000円。差は100円で、Pが加えた1,000円にも10%を付けるかどうかに由来する。直列化可能性が求めるのは、許されたどれかの直列順序と等価になることであり、どの順序でも同じ数値になることではない。これは現実の金利制度ではなく、二つの演算の順序を考える教材である。
強い分離を実現する方法には、競合する仕事を待たせる方法や、問題のある組合せを検出して一方を中止する方法などがあります。Serializableでもすべての仕事が必ず最初の試行で成功するわけではありません。実務では再試行や重複処理への備えも設計しますが、本節では製品の設定や実行手順ではなく、保証の意味だけを扱います。
A = 8,400円、B = 1,600円から2,500円をAからBへ振り替える。成功時と中止・取消し完了時の確定残高を求め、不変条件を確認しよう。他の仕事はないものとする。
解答・解説
成功時はA = 5,900円、B = 4,100円。中止時はA = 8,400円、B = 1,600円。どちらも合計10,000円で残高は非負。引落しと加算の一方だけを確定させない原子性が前提になる。
例題2とは逆に、Rが振替前のAを読み、Tの確定後にBを読んだ。Rの合計はいくらか。実際の台帳の総額が増えたと判断できるか。
解答・解説
10,000 + 8,000 = 18,000円。実際の確定総額は常に15,000円なので、総額が増えたとは判断できない。Rが古いAと新しいBを混ぜ、移った3,000円を二重に数えたためである。
RがA = 10,000円を読み、別の仕事TがA = 7,000円への変更をコミットした。その後、同じRが同じAを読んで7,000円を得た。これはどの読み取り現象か。
解答・解説
非再現読み取り。同じ仕事で同じ行を再読して値が変わっている。二回目に読んだのは確定済みの変更なので、ダーティリードではない。
ある実装のRepeatable Readでは検索結果の行集合も一定だった。「Repeatable Readは一般にファントムを許容し得る」という表と矛盾するか。
解答・解説
矛盾しない。「許容し得る」は保証の下限についての説明で、発生を義務づけていない。その実装が表より強い保証を提供することは可能。ただし行集合が一定という事実だけで直列化可能性まで証明したことにはならない。
問1. 振替の引落しだけを確定させず、引落しと加算を両方確定するか両方取り消す性質は?
問2. 例題2でRが12,000円という合計を得た直接の理由は?
問3. 一般的な分離レベルの表で、ダーティリードは禁止するが非再現読み取りは許容され得るレベルは?
問4. 直列化可能性の説明として正しいものは?
根拠と適用範囲
概念整理の参考文献:Abraham Silberschatz, Henry F. Korth, S. Sudarshan, Database System Concepts, 第7版(2019年)のトランザクション・同時実行制御の説明。本節は教科書的な概念モデルであり、特定製品の現行仕様や性能を報告するものではありません。分離レベル表は伝統的な読み取り現象による整理で、全種類の競合を網羅する仕様表ではありません。残高・振替額・時系列・10%加算はすべて教材用に作成した架空データで、現実の金融制度、統計、障害事例を示していません。