トランザクションと ACID
複数の処理を“全部成功か、全部なかったことにするか”の一塊として扱う仕組みと、それを支える4つの性質 ACID をやさしく解説。
- 1.トランザクションは複数の処理を“ひとまとまり”にして、全部成功(コミット)か全部取り消し(ロールバック)かのどちらかに揃える仕組み。
- 2.信頼性を支える4性質が ACID:原子性(Atomicity)・一貫性(Consistency)・独立性(Isolation)・永続性(Durability)。
- 3.並行実行の“見え方”は分離レベルで調整でき、緩めるほど速いがダーティリードなどの異常が起きやすくなる。
なぜ必要?──送金の例
「A の口座から B の口座へ 1,000 円送る」は、実は2つの更新に分かれます。
UPDATE accounts SET balance = balance - 1000 WHERE id = 'A';
UPDATE accounts SET balance = balance + 1000 WHERE id = 'B';
1行目の直後にサーバが落ちたら、A から引かれたのに B には入らない=お金が消えます。この2行を1つのトランザクションで囲み、両方成功したときだけ確定(コミット)すれば、こうした「片側だけ実行」を防げます。
BEGIN; -- トランザクション開始
UPDATE accounts SET balance = balance - 1000 WHERE id = 'A';
UPDATE accounts SET balance = balance + 1000 WHERE id = 'B';
COMMIT; -- ここで初めて確定
-- 途中で問題があれば COMMIT の代わりに ROLLBACK; で全部取り消し
- コミット(COMMIT): 変更を確定させ、他からも見える正式な状態にする。
- ロールバック(ROLLBACK):
BEGIN以降の変更をすべて破棄し、開始前の状態へ戻す。
ACID とは
トランザクションが満たすべき4つの性質の頭文字です。
| 性質 | 意味 | 送金での具体例 |
|---|---|---|
| 原子性 (Atomicity) | 全部やるか、全部やらないか。中途半端を許さない | 2つの UPDATE は両方反映 or 両方取消 |
| 一貫性 (Consistency) | ルール(制約)を破った状態にはしない | 残高がマイナス禁止なら、その制約は常に保たれる |
| 独立性 (Isolation) | 並行する他の処理から途中経過が見えない | 送金中の半端な残高を別ユーザーは見ない |
| 永続性 (Durability) | コミットしたら障害が起きても消えない | COMMIT 後はサーバ再起動でも結果が残る |
一貫性(C)は、原子性・独立性・永続性と、アプリ側が定義した制約(NOT NULL、外部キー、CHECK など)やロジックの合わせ技で守られます。「残高は0以上」というルールをどこにも書かなければ、DB はマイナス残高を一貫性違反とは見なしません。ルールを宣言して初めて守られる点に注意。
並行実行と分離レベル
DB は普通、多数のトランザクションを同時にさばきます。完全に1件ずつ順番待ち(直列)にすれば安全ですが遅い。そこで「どこまで他の途中経過を見せるか」を段階的に選べるのが 分離レベル(Isolation Level) です。緩いほど速く、厳しいほど安全です。
代表的な異常(アノマリー)は次の3つ。
| 異常 | 何が起きる | ひとことで |
|---|---|---|
| ダーティリード | 他がまだコミットしていない(取消されるかもしれない)変更を読んでしまう | 未確定の値を読む |
| ノンリピータブルリード | 同じ行を2回読むと、間に他のコミットが入り値が変わる | 同じ行の値がブレる |
| ファントムリード | 同じ条件で2回検索すると、間に挿入/削除され行数が変わる | ヒットする件数がブレる |
SQL 標準は、これらをどこまで防ぐかで4つの分離レベルを定めています。
| 分離レベル | ダーティリード | ノンリピータブル | ファントム |
|---|---|---|---|
| READ UNCOMMITTED | 起きうる | 起きうる | 起きうる |
| READ COMMITTED | 防ぐ | 起きうる | 起きうる |
| REPEATABLE READ | 防ぐ | 防ぐ | 起きうる(*) |
| SERIALIZABLE | 防ぐ | 防ぐ | 防ぐ |
上の表は SQL 標準が定める**「最低限ここまでは防ぐ」**という線引きです。実際の DB はもっと強いことが多く、(*) を付けた REPEATABLE READ でも、たとえば PostgreSQL の実装ではファントムリードまで防ぎます。自分が使う製品の分離レベルが実際に何を保証するかは、製品のドキュメントで必ず確認しましょう。デフォルトの分離レベルも製品ごとに違います(多くは READ COMMITTED)。
一番厳しい SERIALIZABLE にすれば異常は消えますが、その分ロック待ちや競合による再試行(シリアライズ失敗)が増え、スループットが落ちたりデッドロックが起きやすくなります。**異常を「どこで・どう防ぐか」**を考え、必要な範囲だけ分離レベルを上げるのが定石です。どう実現されるかはロックとMVCCで扱います。
つまずきポイント
トランザクションを開いている間、行ロックや古いバージョンのデータが保持され続けます。中で外部API呼び出しやユーザー入力待ちをすると、その間ずっと他をブロックしたり DB の掃除(バキューム)を妨げたりします。トランザクションは短く保ち、重い待ち処理は外に出すのが鉄則です。
多くのクライアントは既定で1文ごとに自動コミットします。複数の更新を一塊にしたいのに BEGIN/COMMIT で囲み忘れると、原子性は効かず「1行目だけ確定」が普通に起こります。一塊で扱いたい処理は、明示的にトランザクションで囲っているか確認しましょう。
どんな時に意識する?
- 在庫引き当てと注文確定、ポイント減算と特典付与など、複数テーブル/複数行を矛盾なく更新したいとき。
- 同じデータに多人数が同時アクセスし、競合の見え方を制御したいとき。
- 関連する話として、NoSQL では強い ACID より可用性・スケールを優先する設計もあります(RDB と NoSQL)。また排他制御の中身はロックとMVCCへ。トランザクションは「正しさ」と「速さ」を天秤にかける道具であり、常に最強設定が正解ではありません。
データベース Article
トランザクションと ACIDを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
トランザクション
比較で見る軸
難易度: intermediate / カテゴリ: データベース / タグ数: 4
導入後に効く点
信頼性を支える4性質が ACID:原子性(Atomicity)・一貫性(Consistency)・独立性(Isolation)・永続性(Durability)。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- intermediate
- カテゴリ
- データベース
- タグ数
- 4
判断チェックリスト
- 自社の用途が「トランザクション / ACID」に近いか確認する。
- 強みである「トランザクションは複数の処理を“ひとまとまり”にして、全部成功(コミット)か全部取り消し(ロールバック)かのどちらかに揃える仕組み。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。