トランザクションとは?
トランザクションは、データベースで1つの論理的な作業単位として処理されるべき操作の集まりです。トランザクション内の全操作は全て成功するか全て失敗しなければなりません。
代表的な例:銀行振込
- Aの口座から10,000円を引き出す
- Bの口座に10,000円を入金する
もし1が成功して2でエラーが発生したら、Aの口座からだけお金が消えてしまいます。トランザクションはこのような問題を防ぎます。
BEGIN;
UPDATE accounts SET balance = balance - 10000 WHERE id = 'A';
UPDATE accounts SET balance = balance + 10000 WHERE id = 'B';
COMMIT; -- 両方のUPDATEが成功したら確定
-- またはROLLBACK; -- エラー時は全て元に戻す
ACID特性
トランザクションは4つの核心特性を保証する必要があります。
A — 原子性(Atomicity)
トランザクション内の操作は**「全か無か(All or Nothing)」**です。途中でエラーが発生したら、既に実行された操作も全てロールバックされます。
C — 一貫性(Consistency)
トランザクション実行前後でデータベースは常に一貫した状態を維持しなければなりません。制約条件(NOT NULL、UNIQUE、FK等)がトランザクション後も守られる必要があります。
I — 独立性(Isolation)
複数のトランザクションが同時に実行される際、各トランザクションは他のトランザクションの中間状態を見てはいけません。順次実行されるかのように見えなければなりません。
D — 永続性(Durability)
トランザクションがコミットされた後は、システム障害が発生してもデータが永続的に保存されなければなりません。データベースはWAL(Write-Ahead Log)等の仕組みでこれを保証します。
同時実行の問題
独立性が完全でない場合に発生しうる問題です。
ダーティリード(Dirty Read)
まだコミットされていない他のトランザクションの変更を読んでしまう問題。
反復不可能読み取り(Non-Repeatable Read)
同じクエリを2回実行したとき、他のトランザクションがコミットしたため結果が変わってしまう問題。
ファントムリード(Phantom Read)
同じ範囲クエリを2回実行したとき、他のトランザクションが行を追加・削除したため行数が変わってしまう問題。
分離レベル(Isolation Level)
| 分離レベル | ダーティリード | 反復不可能読み | ファントムリード |
|---|---|---|---|
| READ UNCOMMITTED | 発生 | 発生 | 発生 |
| READ COMMITTED | 防止 | 発生 | 発生 |
| REPEATABLE READ | 防止 | 防止 | 発生 |
| SERIALIZABLE | 防止 | 防止 | 防止 |
-- PostgreSQLで分離レベルを設定
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
-- トランザクション内の操作
COMMIT;
PostgreSQLのデフォルトはREAD COMMITTED、MySQL InnoDBはREPEATABLE READです。
実践的なヒント
- 短く保つ:ロック競合を最小化するためトランザクションは短く設計
- READ COMMITTED:大半のOLTPアプリケーションに適切
- SERIALIZABLE:金融決済など厳密な一貫性が必要な場合