データベースのトランザクション(一連の処理のまとまり)が「安全に終わる」ための4つの約束が ACID です。
- Atomicity(原子性)
- Consistency(一貫性)
- Isolation(独立性/分離性)
- Durability(永続性)
以下、日常例→DBの例→どう実現するか→失敗すると何が起きるか、の順で解説します。
A:Atomicity(原子性)
全部やるか、全部やらないか。途中の中途半端は禁止。
- 日常例:ATMの振込で「Aの口座−1万円」「Bの口座+1万円」は必ずセット。片方だけ反映はダメ。
- DB例:
BEGIN;
UPDATE accounts SET balance = balance - 10000 WHERE id = 'A';
UPDATE accounts SET balance = balance + 10000 WHERE id = 'B';
COMMIT; -- ここまで全部成功で確定
-- どこかで失敗なら ROLLBACK; で全部取り消し
どう実現?:Undoログ(ロールバック用)、エラー時のROLLBACK、自動コミットOFFで明示的にCOMMIT。
崩れると:Aからだけ引かれてBに入らない、などの「お金が消える/増える」事故。
C:Consistency(一貫性)
トランザクション前後で、定義したルール(整合制約)を常に満たす。
- 日常例:在庫がマイナスになってはいけない、会員の誕生日が未来日になってはいけない…など。
- DB例:主キー・外部キー・CHECK制約・ユニーク制約・トリガなどが守られる。
ALTER TABLE orders ADD CONSTRAINT chk_qty CHECK (quantity > 0);
どう実現?:DBの制約(Constraints)・トリガ・アプリの検証ロジック。
注意:ここでいうConsistencyはDB内部の整合性のこと。
“複数サーバ間の同期の話(最終的整合性)”とは別物です。
崩れると:重複キーや在庫マイナスなど「ルール違反のデータ」が生まれる。
I:Isolation(独立性/分離性)
並行に走るトランザクション同士が干渉しないように見せる。
- 日常例:2人が同時に同じ在庫1個の商品を買っても、どちらか一方しか買えないようにする。
- DB例:他トランザクションの未確定変更が見えない、行や範囲にロックを掛ける、MVCCでスナップショットを読む。
- 代表的な異常(防ぎたい現象)
- Dirty Read:未コミットの値を読んでしまう
- Non-Repeatable Read:同じ行を2回読むと値が変わる
- Phantom Read:同じ条件で再検索したら行数が増減
- 隔離レベル(代表4段階・下に行くほど厳格/遅め)
- READ UNCOMMITTED:Dirty Read発生し得る
- READ COMMITTED:Dirty Read防止(多くのDBの既定)
- REPEATABLE READ:同じ行は同じ値で読める(Phantomは起きうる)
- SERIALIZABLE:直列実行と同等(最も安全だが競合で待ち/失敗増)
- どう実現?:行/テーブルロック、MVCC(Multi-Version Concurrency Control)、インデックス+ギャップロックなど。
- 崩れると:在庫ダブル販売、矛盾した読み取り、集計の取りこぼし。
D:Durability(永続性)
COMMITしたら、障害があっても結果は消えない。
- 日常例:保存ボタンを押したら、アプリが落ちてもデータは残っているべき。
- DB例:COMMIT完了=ディスクに安全に書いたことの保証。
- どう実現?:WAL(Write-Ahead Logging:先行書き込みログ)、fsync、チェックポイント、レプリケーション、バックアップ。
- 崩れると:電源断で直前に確定したはずの注文や入金が消える。
まとめイメージ(1行ずつ覚える)
- Atomicity:部分的成功なし(オールorナッシング)
- Consistency:前後で制約は常に真
- Isolation:他の同時処理に振り回されない見え方
- Durability:確定したら消えない
実務でよくあるポイント
- オートコミットに注意:1文ごとに確定されるため、原子性が崩れがち。複数文をまとめるときは
BEGIN … COMMIT
。 - 隔離レベルはトレードオフ:厳しくするほど安全だが待ち/衝突が増え、スループットが落ちる。
- MVCCの読みやすさ:読み取り専用処理はロックを避け、過去版を読むことで高速に。同時更新は衝突時に再試行が必要。
- Durabilityはハード依存も大:ジャーナリングFS、バッテリ付きキャッシュ、ストレージのキャッシュ設定(ライトバック/スルー)で体感が変わる。
- ACID≠バックアップ:ACIDは“その瞬間”の安全。バックアップ/リストアは“過去に戻す”ための別レイヤ。
すぐ試せるミニ演習(SQLite/MySQL/PostgreSQL想定)
- 原子性の確認(途中でエラーを起こしてROLLBACKされるか)
BEGIN;
UPDATE accounts SET balance = balance - 10000 WHERE id='A';
-- 故意に失敗(存在しない列を更新など)
UPDATE accounts SET balanc = balance + 10000 WHERE id='B';
COMMIT; -- エラーで到達しないはず → ROLLBACKされ、Aの残高も元通りか確認
- 隔離の体験(別セッション2つで)
- セッション1:
BEGIN; SELECT balance FROM accounts WHERE id='A';
(長い処理のふりをしてCOMMITしない) - セッション2:
UPDATE accounts SET balance=balance+1 WHERE id='A';
- 隔離レベルで見える/待つ/エラーの挙動が変わるのを確認。
ひとことで言うと
ACIDは「安全に処理をまとまって確定する」ための最低限のルールセット。
検証のカギは「まとめる(BEGIN/COMMIT)」「制約で守る」「隔離レベルを選ぶ」「ログで守り書きする」。