トランザクションとACID — 全部やるか、何もしないか
口座 A から引いて、口座 B へ足す。この 2 つは必ずセットでなければなりません。片方だけ実行された瞬間にお金が消える(か、増える)からです。「途中で止まらない一連の操作」=トランザクションと、それが守る 4 つの約束 ACID を、送金とクラッシュで体感します。
1. 送金は「分けられない一連の操作」
A の残高を減らし、B の残高を増やす。正常に最後まで進めば(コミット)、A+B の総額は変わりません。お金はどこにも生まれず、消えもしない——これが送金の大前提です。
「送金を実行」を押して、お金が A から B へ移り、総額が保たれる様子を見てください。
正常な送金 — 総額は保たれる
飛んでいく黄色いコイン=移動中の 300 円。移動の途中でも A + 移動中 + B = 1500 円で一定。BEGIN → 引き落とし → 入金 → COMMIT の 4 手順が順に光ります。
POINT — トランザクションの単位
トランザクションは「これ以上分けられない仕事のかたまり」。
BEGIN で始め、COMMIT で確定、ROLLBACK で取り消す。中の複数の更新は、外から見ればひとつの操作として全部成功するか、全部なかったことになる。
BEGIN → 引き落とし(A) → 入金(B) → COMMIT
この 4 つは一体。途中の状態(A だけ減った状態)は、外の誰にも見せない・残さない
2. クラッシュの瞬間 — 原子性がすべてを救う
では、引き落としの直後・入金の前にサーバが落ちたら? トランザクションで囲んでいなければ、A は減ったのに B は増えず、お金が消えます(不整合)。囲んでいれば、再起動時に巻き戻し(ROLLBACK)で元の整合した状態に戻ります。
スライダーでクラッシュのタイミングを選び、トランザクションの ON/OFF を切り替えて「送金を実行」してみてください。危険区間(引落済・入金前)での結果の違いが核心です。
クラッシュ注入 — 原子性のある/なし
下の帯=トランザクションの進行。赤い区間が危険区間(A は引落済・B は未入金)。ここで落ちると、囲みなしは総額が減って不整合、囲みありは巻き戻しで総額 1500 円に復帰します。
注意 — 「片方だけ成功」こそ最悪
全部成功はもちろん OK。全部失敗も、やり直せば済むので実は OK。本当に困るのは中途半端に片方だけ成功すること。原子性(Atomicity)は、この「片方だけ」という状態をこの世に残さないという保証。だから障害があっても、データは常に「送金前」か「送金後」のどちらかにいる。
3. ACID — トランザクションの4つの約束
トランザクションが守る性質は、頭文字を取って ACID。原子性・一貫性・独立性・永続性。ボタンで 1 つずつ、あるいは自動で巡回するミニ図で、それぞれのイメージをつかみましょう。
ACID ミニ図鑑 — 4つの性質
原子性=2つの操作は一緒に点く/消える(全部 or 無)。一貫性=総額の天秤は常に釣り合う。独立性=同時実行しても互いのレーンに干渉しない。永続性=コミット済みは雷(クラッシュ)が来ても消えない。ボタンで注目、放っておくと自動で巡回します。
4. 独立性の実演 — 同時更新でお金が消える
同じ口座から 2 人が同時に引き出したら? 独立性(分離)がないと、両方が「残高 1000 円」を読んでから各自 300 円引き、片方の引き出しが上書きで消えます(ロストアップデート)。ロックで直列化すれば、正しく 400 円になります。
ロストアップデート — 分離あり/なし
T1・T2 が読み込み(Read)→ 書き込み(Write)を行います。分離なしは両者が古い残高を読み、後の書き込みが前を上書き→700円(1回分が消失)。分離ありは T2 が待たされ、正しく400円になります。
POINT — 分離レベルという調整つまみ
完全な分離(直列化)は安全だが、待ち行列が増えて遅くなる。だから DB は分離レベル(READ COMMITTED / REPEATABLE READ / SERIALIZABLE …)で「どこまで厳密に分けるか」を選べる。安全性と性能のトレードオフ。詳しいしくみ(ロック・2PL・MVCC)は次の「並行制御」で扱う。
5. まとめ — 壊れないデータの土台
- トランザクション:BEGIN〜COMMIT で囲む「分けられない仕事のかたまり」。全部やるか、何もしないか。
- 原子性(A):途中でクラッシュしても「片方だけ成功」を残さない。巻き戻しで整合状態へ。
- 一貫性(C):総額や制約などの不変条件が、トランザクションの前後で常に保たれる。
- 独立性(I):同時実行しても、あたかも順番に実行したかのような結果になる。
- 永続性(D):コミットした結果は、その後クラッシュしても失われない。
一歩先へ — 「巻き戻せる」のはログのおかげ
ROLLBACK や再起動時の復旧を可能にしているのがログ先行書き込み(WAL)だ。DB は実データを書き換える前に、「何をどう変えるか」をログへ順番に書き出す。だからクラッシュ後も、ログを読み直して未完了を取り消し(UNDO)、コミット済みを再実行(REDO)できる。原子性と永続性は、この地味なログが下支えしている——その全貌は「障害回復とログ」の章で。