SQLのトランザクション(TRANSACTION)を完全ガイド!初心者でもわかるデータの一貫性の守り方
新人
「データベースの処理中にエラーが発生したとき、データが中途半端に登録されたりするのを防ぐ方法はありますか?」
先輩
「それならトランザクション(TRANSACTION)を使うといいよ!トランザクションを利用すると、複数のSQLをひとまとまりの処理として扱うことができるんだ。」
新人
「具体的にはどういう仕組みなんですか?」
先輩
「例えば、銀行の送金処理を考えてみよう。Aさんの口座からお金を引き出して、Bさんの口座に入金するよね?もし途中でエラーが起きてAさんの口座からお金が引き出されたのに、Bさんの口座に入金されなかったら大変だよね?」
新人
「確かに、それは大問題ですね!」
先輩
「そういうデータの一貫性を守るためにトランザクションが使われるんだ。もし処理の途中で問題が発生したら、すべての変更を取り消すことができるよ。」
1. トランザクションとは?
トランザクション(TRANSACTION)とは、データベースにおいて一連の処理をひとまとまりとして管理する仕組みです。すべての処理が成功した場合のみデータを確定(COMMIT)し、途中でエラーが発生した場合は変更を取り消す(ROLLBACK)ことができます。
トランザクションの特徴
- 一貫性を保つ - データが不整合にならないようにする
- エラー時に変更を取り消せる - 途中で問題が発生したら元に戻せる
- 複数の処理をまとめて実行 - すべて成功しないとデータを確定しない
例えば、銀行の送金処理を考えたとき、以下のような処理が行われます。
- Aさんの口座から 1,000円 を引き出す
- Bさんの口座に 1,000円 を入金する
- 両方の処理が成功したら、変更を確定(
COMMIT)する - 途中でエラーが発生したら、すべての変更を取り消し(
ROLLBACK)する
2. トランザクションの開始と終了(BEGIN, COMMIT, ROLLBACK)
トランザクションは、BEGINで開始し、正常に完了した場合はCOMMIT、問題が発生した場合はROLLBACKを使って変更を取り消します。
1. トランザクションの基本構文
-- トランザクションの開始
BEGIN;
-- Aさんの口座から1,000円引き出す
UPDATE accounts SET balance = balance - 1000 WHERE name = 'Aさん';
-- Bさんの口座に1,000円入金する
UPDATE accounts SET balance = balance + 1000 WHERE name = 'Bさん';
-- すべての処理が成功したら確定
COMMIT;
このSQLでは、Aさんの口座から1,000円を引き出し、Bさんの口座に入金する処理をBEGINで開始し、成功したらCOMMITで確定します。
2. トランザクションを途中で取り消す(ROLLBACK)
もし途中でエラーが発生した場合は、ROLLBACKを実行することで、すべての処理を取り消すことができます。
-- トランザクションの開始
BEGIN;
-- Aさんの口座から1,000円引き出す
UPDATE accounts SET balance = balance - 1000 WHERE name = 'Aさん';
-- ここでエラーが発生!(Bさんの口座が存在しない場合)
UPDATE accounts SET balance = balance + 1000 WHERE name = 'Bさん';
-- エラー発生時にすべての変更を取り消す
ROLLBACK;
この場合、Bさんの口座に入金する処理でエラーが発生すると、Aさんの口座から引き出した1,000円も元に戻ります。
3. トランザクションの成功・失敗によるデータの変化
| 操作 | Aさんの残高 | Bさんの残高 | 結果 |
|---|---|---|---|
| トランザクション開始 | 50,000円 | 30,000円 | 処理開始 |
| 1000円引き出し | 49,000円 | 30,000円 | 途中処理 |
| エラー発生(Bさんの口座なし) | 49,000円 | 30,000円 | ROLLBACK |
| トランザクション終了 | 50,000円 | 30,000円 | データは元通り |
このように、トランザクションを使うことでデータの整合性を確保できます。
4. トランザクションのACID特性(原子性、一貫性、独立性、耐久性)
トランザクションが正しく動作するためには、ACID特性(Atomicity, Consistency, Isolation, Durability)を満たす必要があります。
1. 原子性(Atomicity)
すべての処理がすべて成功するかすべて失敗するかのどちらかになる特性です。
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE name = 'Aさん';
UPDATE accounts SET balance = balance + 1000 WHERE name = 'Bさん';
-- ここでエラー発生
ROLLBACK;
この場合、どちらかの処理が失敗すると、すべての変更が取り消されます。
2. 一貫性(Consistency)
データの整合性が保たれることを意味します。例えば、銀行の送金でAさんの口座からお金が引かれたのに、Bさんに入金されないと一貫性が崩れます。
3. 独立性(Isolation)
複数のトランザクションが同時に実行されても、お互いに影響を受けないことを保証します。
4. 耐久性(Durability)
トランザクションが完了したら、システム障害が発生してもデータが消えないことを保証します。
5. トランザクションの自動コミットと手動コミットの違い
デフォルトでは、SQLは自動コミットが有効になっていますが、手動コミットを使用するとトランザクションを明示的に制御できます。
1. 自動コミット(Auto Commit)
通常のSQL操作は、実行されるたびに自動的に確定(COMMIT)されます。
UPDATE accounts SET balance = balance - 1000 WHERE name = 'Aさん';
-- 変更はすぐにデータベースに反映される
2. 手動コミット(Manual Commit)
トランザクションを手動で管理すると、エラー時に変更を取り消すことができます。
SET AUTOCOMMIT = 0;
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE name = 'Aさん';
UPDATE accounts SET balance = balance + 1000 WHERE name = 'Bさん';
COMMIT;
この場合、すべての処理が成功した後にCOMMITすることでデータを確定できます。
6. SAVEPOINTを使った部分ロールバック
SAVEPOINTを使うと、トランザクションの途中で部分的に変更を取り消すことができます。
1. SAVEPOINTの基本
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE name = 'Aさん';
SAVEPOINT sp1;
UPDATE accounts SET balance = balance + 1000 WHERE name = 'Bさん';
ROLLBACK TO sp1; -- Bさんの入金を取り消す
COMMIT;
この場合、Aさんの口座から引き出しは維持しつつ、Bさんへの入金だけを取り消します。
2. SAVEPOINTの応用
複数のSAVEPOINTを設定し、どの時点までロールバックするかを選択できます。
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE name = 'Aさん';
SAVEPOINT sp1;
UPDATE accounts SET balance = balance + 1000 WHERE name = 'Bさん';
SAVEPOINT sp2;
UPDATE accounts SET balance = balance + 500 WHERE name = 'Cさん';
ROLLBACK TO sp2; -- Cさんの入金だけを取り消す
COMMIT;
このSQLでは、Aさんの引き出しとBさんの入金は維持しつつ、Cさんの入金だけを取り消します。
このようにSAVEPOINTを活用することで、トランザクションを細かく管理できます。
7. トランザクションとロックの関係(排他制御とは?)
トランザクションを使用する際、データの整合性を保つためにロック(Lock)が重要な役割を果たします。ロックを適切に使うことで、データの競合を防ぎ、安全なデータ更新を実現できます。
1. 排他制御とは?
データベースにおいて、複数のユーザーが同じデータを同時に更新しようとすると、データの不整合が発生する可能性があります。これを防ぐために、排他制御(Exclusive Control)が必要になります。
2. ロックの種類
- 共有ロック(Shared Lock) - データの読み取りは許可されるが、書き込みは許可されない
- 排他ロック(Exclusive Lock) - データの読み取り・書き込みの両方が禁止される
3. ロックの実装方法
SQLでは、明示的にロックを設定することも可能です。以下のSQLは、FOR UPDATEを使用して特定の行に排他ロックをかける例です。
BEGIN;
SELECT * FROM employee WHERE id = 1 FOR UPDATE;
-- 他のトランザクションは、この行の更新が完了するまで待機する
UPDATE employee SET salary = salary + 5000 WHERE id = 1;
COMMIT;
この処理が完了するまで、他のユーザーはid = 1の行を更新することができません。
8. トランザクション分離レベルの違い(READ COMMITTED, SERIALIZABLEなど)
トランザクションの分離レベルは、複数のトランザクションが同時に実行される際のデータの読み書きの制御を決定します。
1. 分離レベルとは?
トランザクションの分離レベルは、データの一貫性と並行処理のバランスを決める重要な要素です。一般的に、以下の4つのレベルがあります。
| 分離レベル | 許可される動作 | データの整合性 |
|---|---|---|
| READ UNCOMMITTED | 他のトランザクションが確定していないデータを読み取れる | 低い(ダーティリード発生) |
| READ COMMITTED | 確定(COMMIT)されたデータのみ読み取れる | 中程度(リピート可能リード発生) |
| REPEATABLE READ | 同じトランザクション中は、一度読んだデータが変わらない | 高い(ファントムリード発生) |
| SERIALIZABLE | 完全に直列化される(他のトランザクションをブロック) | 非常に高い(最も厳密な整合性) |
2. 分離レベルの設定
デフォルトでは、多くのデータベースでREAD COMMITTEDが使用されます。分離レベルを変更する場合は、以下のSQLを使用します。
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
UPDATE employee SET salary = salary + 5000 WHERE id = 1;
COMMIT;
この場合、他のトランザクションはCOMMITが完了するまでデータを読み取ることができません。
9. 実践!トランザクションを活用したデータ整合性の確保
最後に、実践的なトランザクションの活用例を紹介します。
1. 商品在庫管理のトランザクション
例えば、ECサイトでの注文処理では、以下のようにトランザクションを活用できます。
BEGIN;
-- 在庫があるか確認
SELECT stock FROM products WHERE id = 1 FOR UPDATE;
-- 在庫が1以上なら減らす
UPDATE products SET stock = stock - 1 WHERE id = 1 AND stock > 0;
-- 注文履歴に追加
INSERT INTO orders (product_id, user_id, order_date) VALUES (1, 123, NOW());
-- すべての処理が成功したら確定
COMMIT;
このように、トランザクションを使用することで、在庫の整合性を保つことができます。
2. トランザクション活用時の注意点
- 長時間のトランザクションは避ける - ロックが長引くと、他の処理が待機状態になる
- 適切な分離レベルを選択する - 必要以上に厳格なレベルを設定すると、パフォーマンスが低下する
- エラー時の処理を明確にする -
ROLLBACKの適用を忘れない
3. トランザクションのまとめ
| 処理 | 目的 | コマンド |
|---|---|---|
| トランザクション開始 | 変更を一時的に保持 | BEGIN |
| 変更を確定 | データを確実に保存 | COMMIT |
| 変更を取り消し | エラー時に元の状態に戻す | ROLLBACK |
| 一部の変更を取り消し | 部分的なロールバック | SAVEPOINT / ROLLBACK TO |
このように、トランザクションを適切に活用することで、安全かつ整合性のあるデータ管理が可能になります。