🗄️ 第四章 管理数据-避免非法数据的功能
📄 一句话概括

回滚(ROLLBACK)是事务的后悔药:BEGIN 之后的操作全部作废,数据库回到 BEGIN 前一刻的原样——已经执行的 UPDATE/DELETE 也照样撤得干干净净,像从没发生过。

📝 本节和前面几节的关系

COMMIT(4-15)是「全对,转正」,ROLLBACK 是「出事,作废」——同一枚硬币的两面。本节讲清:为什么能撤(草稿区机制)、什么时机撤(程序里先检查后提交)、以及撤不干净的例外(COMMIT 过了、DDL 语句、自增号)。

一、为什么能撤:草稿区

BEGIN 之后,改动都写在「草稿区」(日志+缓存),原表一字未动。ROLLBACK 的动作就是把草稿一扔——原样自然还在。所以:

二、两种触发方式

1. 手动:代码里先检查后决定

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE name = '甲';
UPDATE accounts SET balance = balance + 100 WHERE name = '乙';
-- 检查:乙的账户被冻结了!
ROLLBACK;    -- 两步全作废

2. 自动:出错时数据库自己撤

BEGIN;
UPDATE t SET a = 1 WHERE id = 1;
UPDATE t SET b = '类型全错的值' WHERE id = 2;   -- 这条报错
-- 事务进入失败态,后续会被要求 ROLLBACK

程序设计惯例:出错必 ROLLBACK,没错才 COMMIT——只有这两条出口,没有第三条。

三、回滚撤不掉的三样东西

撤不掉 原因
已 COMMIT 的 已经转正,草稿区早清空了
DDL 语句(部分数据库) DROP TABLE 等建删结构的操作多半隐式提交,事务管不着
自增编号空洞 号已发出(4-11 伏笔回收),回滚不回收

四、动手 Demo:撤得干净 vs 撤不回

CREATE TABLE t (id INT, n INT);
INSERT INTO t VALUES (1, 100);

-- 撤得干净
BEGIN;
UPDATE t SET n = 0 WHERE id = 1;
ROLLBACK;
SELECT * FROM t;   -- n 还是 100 ✅

-- 撤不回:COMMIT 过了
BEGIN;
UPDATE t SET n = 50 WHERE id = 1;
COMMIT;
ROLLBACK;          -- 晚了!COMMIT 后新事务里没有东西可撤
SELECT * FROM t;   -- n = 50

第二个场景里 ROLLBACK 不报错,但撤的是个空事务——很多事故正是「以为还没提交,其实已提交」。

五、30 秒实验

BEGIN;
DELETE FROM t;          -- 全删了!
SELECT * FROM t;        -- 空,吓一跳
ROLLBACK;               -- 冷静
SELECT * FROM t;        -- 完璧归赵

亲手体验「误删全表也能救回」——这就是事务给的底气(前提:没 COMMIT)。

六、常见误区

自测三问 1. 为什么回滚能撤得干净?(改动只在草稿区,原表未动) 2. 三样撤不掉的是什么?(已提交的/DDL/自增号空洞) 3. 程序里事务的出口有哪两条?(出错 ROLLBACK,没错 COMMIT)

相关笔记