データベースで作業中に「しまった!間違えて大事なデータを削除してしまった…」という経験はありませんか?
Excelなら「元に戻す(Ctrl+Z)」で簡単にやり直せますが、データベースでも同じようなことができるんです。それが「ロールバック(Rollback)」機能です。
ロールバックは、実行した操作を取り消して、処理を始める前の状態に戻す機能のこと。まるでゲームのセーブポイントからやり直すような感覚ですね。
この記事では、ロールバック機能の基本から実践的な使い方まで、初心者にもわかりやすく解説していきます。
ロールバックとは何か

変更を取り消す機能
ロールバックは、英語で「roll back」、つまり「巻き戻す」という意味です。
データベースにおいては、実行した処理を取り消して、元の状態に戻す操作を指します。例えば:
- 誤って削除したデータを復元
- 間違った更新を取り消す
- 途中でエラーが起きた処理を無かったことにする
「やり直しボタン」のようなイメージですね。
トランザクションとの関係
ロールバックを理解するには、「トランザクション」という概念が重要です。
トランザクション:
一連の処理をまとめた単位のこと。「全部成功するか、全部失敗するか」という原則で動きます。
銀行のATMで例えてみましょう:
- A口座から10,000円引き出す
- B口座に10,000円入金する
この2つの処理は、両方成功して初めて意味があります。1つ目だけ成功して2つ目が失敗すると、お金が消えてしまいますよね。
トランザクションは、こうした「分けられない処理」を1つの単位として扱います。
COMMITとROLLBACKの2つの選択
トランザクションの最後には、2つの選択肢があります。
COMMIT(コミット):
変更を確定して、データベースに永久的に保存する
ROLLBACK(ロールバック):
変更を取り消して、トランザクション開始前の状態に戻す
料理で例えるなら:
- COMMIT = 「完成!お皿に盛り付けて提供」
- ROLLBACK = 「失敗…材料を元に戻してやり直し」
データベースにおけるロールバックの仕組み
トランザクションログ
データベースは、すべての変更を「トランザクションログ」に記録しています。
記録される内容:
- いつ
- 誰が
- どのデータを
- どう変更したか
この記録があるから、ロールバックで元に戻せるんです。日記のように、すべての変更履歴が書かれているイメージですね。
BEFORE IMAGEとAFTER IMAGE
変更前のデータ(BEFORE IMAGE)と変更後のデータ(AFTER IMAGE)の両方を記録します。
例:社員テーブルの給与更新
- BEFORE IMAGE:給与 = 300,000円
- 実行:UPDATE employees SET salary = 350,000
- AFTER IMAGE:給与 = 350,000円
ロールバックするときは、BEFORE IMAGEを使って元に戻します。
UNDOセグメント
Oracle DatabaseなどのDBMSは、「UNDOセグメント」という特別な領域に変更前のデータを保存します。
UNDOセグメントの役割:
- ロールバック時にデータを復元
- トランザクションの一貫性を保つ
- 読み取り一貫性を提供
ゴミ箱のようなものと考えると分かりやすいでしょう。削除したファイルがゴミ箱に残っているように、変更前のデータがUNDOセグメントに保存されています。
ロールバックの基本的な使い方
明示的なトランザクション制御
ロールバックを使うには、トランザクションを明示的に開始する必要があります。
基本的な流れ:
-- トランザクション開始
BEGIN TRANSACTION; -- SQL Serverの場合
-- または
START TRANSACTION; -- MySQL、PostgreSQLの場合
-- データ操作
UPDATE products SET price = price * 1.1 WHERE category = '書籍';
-- 確認して問題があれば
ROLLBACK; -- 取り消し
-- 問題なければ
COMMIT; -- 確定
単純なROLLBACKの例
誤ってデータを削除してしまった場合です。
シナリオ:全社員のボーナスを間違って削除
-- トランザクション開始
BEGIN TRANSACTION;
-- 操作実行
DELETE FROM bonuses; -- あ、間違えた!
-- 件数を確認
SELECT COUNT(*) FROM bonuses; -- 0件になっている
-- 慌ててロールバック
ROLLBACK;
-- 確認
SELECT COUNT(*) FROM bonuses; -- 元の件数に戻っている
COMMITする前なら、ロールバックで元に戻せます。
条件付きロールバック
処理の途中でエラーが起きた場合の例です。
BEGIN TRANSACTION;
-- 在庫を減らす
UPDATE inventory SET quantity = quantity - 10
WHERE product_id = 101;
-- 在庫確認
IF (SELECT quantity FROM inventory WHERE product_id = 101) < 0 THEN
-- マイナスになった!在庫不足
ROLLBACK;
PRINT 'エラー:在庫が不足しています';
ELSE
-- 問題なし
COMMIT;
PRINT '出荷処理が完了しました';
END IF;
条件に応じて、COMMITするかROLLBACKするか判断できます。
実践的な使用例
オンラインショッピングの注文処理
複数のテーブルを更新する典型的な例です。
BEGIN TRANSACTION;
-- 1. 在庫を減らす
UPDATE products SET stock = stock - 1
WHERE product_id = 123;
-- 2. 注文レコードを追加
INSERT INTO orders (customer_id, product_id, quantity)
VALUES (456, 123, 1);
-- 3. 顧客のポイントを減算
UPDATE customers SET points = points - 100
WHERE customer_id = 456;
-- すべて成功したら確定
COMMIT;
-- エラーが起きたら全部取り消し
-- ROLLBACK; -- エラー処理で実行
この3つの処理は、すべて成功するか、すべて失敗するかのどちらかです。途中で失敗したら、ROLLBACKですべて元に戻します。
銀行の送金処理
最も厳密な一貫性が求められる例です。
BEGIN TRANSACTION;
-- 送金元の口座から引き出し
UPDATE accounts SET balance = balance - 10000
WHERE account_id = 'A001';
-- 残高がマイナスになっていないか確認
IF (SELECT balance FROM accounts WHERE account_id = 'A001') < 0 THEN
ROLLBACK;
RAISE_ERROR('残高不足です');
RETURN;
END IF;
-- 送金先の口座に入金
UPDATE accounts SET balance = balance + 10000
WHERE account_id = 'B002';
-- 送金履歴を記録
INSERT INTO transfer_history (from_account, to_account, amount, transfer_date)
VALUES ('A001', 'B002', 10000, CURRENT_TIMESTAMP);
-- すべて成功したら確定
COMMIT;
どこかでエラーが起きたら、ROLLBACKですべてなかったことにします。
データ移行作業
大量のデータを移行するときの例です。
BEGIN TRANSACTION;
-- 旧テーブルから新テーブルにデータをコピー
INSERT INTO new_customers (customer_name, email, phone)
SELECT name, email_address, phone_number
FROM old_customers;
-- 件数を確認
DECLARE @old_count INT, @new_count INT;
SELECT @old_count = COUNT(*) FROM old_customers;
SELECT @new_count = COUNT(*) FROM new_customers;
IF @old_count = @new_count THEN
-- 件数が一致したので確定
COMMIT;
PRINT '移行が正常に完了しました';
ELSE
-- 件数が合わないのでロールバック
ROLLBACK;
PRINT 'エラー:データ件数が一致しません';
END IF;
慎重にデータを移行する場合に、このような確認とロールバックの組み合わせが有効です。
SAVEPOINT(セーブポイント)の活用

部分的なロールバック
トランザクション全体ではなく、途中の地点まで戻したい場合があります。
SAVEPOINT:
トランザクションの途中にチェックポイントを設定する機能
ゲームのセーブポイントとまったく同じ概念です。
使い方:
BEGIN TRANSACTION;
-- 処理1
INSERT INTO logs (message) VALUES ('処理1完了');
-- セーブポイント作成
SAVEPOINT point1;
-- 処理2
UPDATE products SET price = price * 1.1;
-- 処理2を取り消したい
ROLLBACK TO point1; -- point1まで戻る
-- 処理3を実行
DELETE FROM temp_data;
-- 全体を確定
COMMIT;
この場合、処理2だけが取り消されて、処理1と処理3は残ります。
複雑な処理での活用
複数のステップがある処理で便利です。
BEGIN TRANSACTION;
-- ステップ1:顧客情報の更新
UPDATE customers SET address = '新住所' WHERE customer_id = 100;
SAVEPOINT after_customer_update;
-- ステップ2:注文情報の更新
UPDATE orders SET shipping_address = '新住所' WHERE customer_id = 100;
SAVEPOINT after_order_update;
-- ステップ3:配送情報の更新
UPDATE shipments SET delivery_address = '新住所' WHERE customer_id = 100;
-- ステップ3でエラーが起きたら、ステップ2の後まで戻る
-- ROLLBACK TO after_order_update;
-- すべて成功したら確定
COMMIT;
段階的に処理を進めながら、問題があれば特定のポイントまで戻せます。
自動ロールバック
エラー発生時の自動ロールバック
多くのデータベースは、エラーが起きると自動的にロールバックします。
自動ロールバックが起きる例:
BEGIN TRANSACTION;
-- データ挿入
INSERT INTO employees (id, name, salary)
VALUES (1, '山田太郎', 300000);
-- 同じIDで再度挿入(主キー違反)
INSERT INTO employees (id, name, salary)
VALUES (1, '佐藤花子', 350000); -- エラー!
-- この時点で自動的にROLLBACKされる
-- 最初のINSERTも取り消される
明示的にROLLBACKを書かなくても、エラーで自動的に巻き戻ります。
デッドロックによる自動ロールバック
複数のトランザクションが互いにロックを待ち合う「デッドロック」が発生すると、データベースが自動的に片方をロールバックします。
デッドロックの例:
トランザクション1:
-- テーブルAをロック
UPDATE table_a SET value = 1 WHERE id = 1;
-- テーブルBをロックしようとする(待機)
UPDATE table_b SET value = 2 WHERE id = 2;
トランザクション2:
-- テーブルBをロック
UPDATE table_b SET value = 3 WHERE id = 2;
-- テーブルAをロックしようとする(待機)
UPDATE table_a SET value = 4 WHERE id = 1;
お互いに相手が持っているロックを待つため、永遠に進まなくなります。データベースはこれを検出して、片方を自動的にロールバックして解決します。
ロールバックの制約と注意点
COMMITした後は戻せない
最も重要な注意点です。
BEGIN TRANSACTION;
DELETE FROM important_data; -- データ削除
COMMIT; -- 確定してしまった!
ROLLBACK; -- もう遅い…戻せない
COMMITすると、変更が永久的に確定されます。その後はロールバックできません。
DDL文は自動的にCOMMIT
テーブルの作成や削除などのDDL(Data Definition Language)文は、実行すると自動的にCOMMITされることが多いです。
例(MySQLの場合):
BEGIN TRANSACTION;
INSERT INTO products VALUES (1, '商品A', 1000);
CREATE TABLE new_table (id INT); -- 自動COMMIT!
ROLLBACK; -- INSERTは戻るが、CREATE TABLEは戻らない
データベースによって動作が異なるので、注意が必要です。
ロールバックにも時間がかかる
大量のデータを変更した後のロールバックは、時間がかかります。
理由:
- UNDOセグメントから大量のデータを読み込む
- 元の状態に書き戻す処理が必要
- インデックスの再構築も発生
大規模な更新を行う前は、本当に実行してよいか慎重に確認しましょう。
UNDOセグメント領域の不足
ロールバック用のデータを保存する領域が不足すると、エラーになることがあります。
エラー例(Oracle):
ORA-30036: unable to extend segment by 8 in undo tablespace 'UNDOTBS1'
対策:
- トランザクションを小さく分割
- UNDO領域を拡張
- 定期的にCOMMIT
ソフトウェア開発におけるロールバック
アプリケーションのバージョンロールバック
データベースだけでなく、ソフトウェアの更新でもロールバックという言葉を使います。
使用例:
- 新しいアプリのバージョンに不具合があった
- 前のバージョンに戻す(ロールバック)
- 問題を修正してから再度アップデート
スマホアプリの更新で不具合が出たときに、前のバージョンに戻すようなイメージです。
Gitのロールバック
バージョン管理システムGitでも、コミットを取り消す操作をロールバックと呼びます。
Gitのロールバック方法:
# 直前のコミットを取り消し
git revert HEAD
# 特定のコミットまで戻る
git reset --hard <コミットID>
プログラムの変更を元に戻したいときに使います。
クラウドサービスのロールバック
クラウド上のシステムでも、設定変更や更新のロールバックができます。
例:
- AWSのCloudFormationスタック更新の失敗時の自動ロールバック
- Kubernetesのデプロイメントロールバック
- Azure App Serviceのスロット切り替え
本番環境の更新に失敗したときの「緊急脱出ボタン」です。
ロールバックのベストプラクティス

トランザクションを適切な大きさにする
トランザクションは、大きすぎても小さすぎても問題です。
適切なトランザクション:
- 論理的に分けられない処理をまとめる
- 長時間ロックを保持しない
- 必要最小限の範囲にする
悪い例:
BEGIN TRANSACTION;
-- 100万件のデータを更新
UPDATE huge_table SET status = 'processed';
-- 数時間かかる…
COMMIT;
良い例:
-- 1万件ずつ分割
FOR i IN 1..100 LOOP
BEGIN TRANSACTION;
UPDATE huge_table SET status = 'processed'
WHERE id BETWEEN i*10000 AND (i+1)*10000;
COMMIT;
END LOOP;
COMMITの前に必ず確認
特に本番環境では、COMMITの前に必ず結果を確認しましょう。
確認の流れ:
BEGIN TRANSACTION;
-- 更新実行
UPDATE employees SET salary = salary * 1.1
WHERE department = '営業部';
-- 影響を確認
SELECT COUNT(*), AVG(salary) FROM employees
WHERE department = '営業部';
-- 問題なければ
COMMIT;
-- 問題あれば
-- ROLLBACK;
テスト環境で試す
本番環境でいきなり実行せず、必ずテスト環境で試しましょう。
手順:
- テスト環境で実行
- 結果を確認
- 問題なければ本番環境で実行
- 本番でも結果を確認してからCOMMIT
慎重すぎるくらいがちょうどいいです。
バックアップは別途取得
ロールバック機能があっても、バックアップは必須です。
理由:
- COMMITした後は戻せない
- システム障害でロールバックできないこともある
- 長期的な復旧にはバックアップが必要
ロールバックは「直前の操作の取り消し」、バックアップは「長期的な保険」と考えましょう。
よくあるトラブルと解決方法
ロールバックできない
エラー: 「no transaction is active」
原因:
- トランザクションが開始されていない
- すでにCOMMITまたはROLLBACKされている
解決方法:
- BEGIN TRANSACTIONを実行してからデータ操作
- 自動コミットモードになっていないか確認
ロールバックが遅い
問題: ロールバックに何時間もかかる
原因:
- 大量のデータを変更した
- UNDOセグメントからの読み込みに時間がかかる
解決方法:
- ロールバックが完了するまで待つ(中断すると危険)
- 次回からトランザクションを小さく分割
- 処理の前に十分な計画を立てる
デッドロックエラー
エラー: 「Deadlock found when trying to get lock」
原因:
- 複数のトランザクションが互いにロックを待っている
解決方法:
- 自動ロールバックされたトランザクションを再実行
- テーブルのアクセス順序を統一
- トランザクションを短くする
まとめ:ロールバック機能でデータの安全性を確保
ロールバック機能は、データベースの安全性を支える重要な仕組みです。
この記事のポイント:
- ロールバックは変更を取り消して元に戻す機能
- トランザクション単位で管理される
- COMMITで確定、ROLLBACKで取り消し
- SAVEPOINTで部分的なロールバックが可能
- エラー時は自動的にロールバックされることもある
- COMMITした後は戻せないので注意
- 適切な大きさのトランザクションが重要
安全にデータを操作するための心得:
- 必ずトランザクション内で操作する
- COMMITの前に結果を確認
- テスト環境で事前に試す
- 大規模な処理は分割する
- バックアップは別途取得
ロールバック機能は「やり直しボタン」として非常に便利ですが、万能ではありません。COMMITの前に必ず確認する習慣をつけて、慎重に操作しましょう。
データは企業の大切な資産です。ロールバック機能を正しく理解して、安全なデータベース操作を心がけてくださいね!

コメント