回滚靠的是 InnoDB 的 Undo Log,不是“读旧数据”
MySQL 本身不存历史快照,
ROLLBACK能撤销修改,全靠 InnoDB 在事务执行时同步写入的
Undo Log。它不是备份,而是“反向操作指令”: -
INSERT回滚 → 记录主键,回滚时执行
DELETE-
UPDATE回滚 → 保存旧值,回滚时用旧值覆盖当前行 -
DELETE回滚 → 记录整行内容,回滚时重新
INSERT
这些日志按时间逆序执行,所以大事务回滚可能卡顿,且会持续占用
undo tablespace。MyISAM 完全没这套机制——它压根不写
Undo Log,所以
ROLLBACK对它无效。
为什么 ROLLBACK 有时像没生效?三个检查点必须做
回滚失败,90% 是因为事务根本没真正“活”着。执行前务必确认: -
SELECT @@in_transaction;返回
1才表示当前有未提交事务 -
SELECT @@autocommit;返回
0(或已用
START TRANSACTION显式开启) - 表引擎是
InnoDB:运行
SHOW CREATE TABLE table_name;看
ENGINE=InnoDB
常见假象: - 直接写
UPDATE后跟
ROLLBACK→ 失败,因为
autocommit=1下每句都已提交 - 执行过
ALTER TABLE或
CREATE INDEX→ 隐式触发
COMMIT,之后的
ROLLBACK只能回滚它之后的操作 - 客户端异常断连 → 若
autocommit=1,所有语句早已落盘,无法挽回
SAVEPOINT 不是“子事务”,只是回滚锚点
SAVEPOINT解决不了嵌套逻辑,它只是在当前事务里打个标记,供
ROLLBACK TO SAVEPOINT使用: -
SAVEPOINT before_update;-
UPDATE ...;-
ROLLBACK TO SAVEPOINT before_update;→ 仅撤销
UPDATE,保留之前所有操作
注意: -
SAVEPOINT不隔离锁、不创建新事务上下文 - 同名
SAVEPOINT会覆盖前一个,不存在“栈式管理” -
RELEASE SAVEPOINT sp_name;只是释放标记,不影响数据
自动回滚只发生在底层错误,业务错要自己兜底
MySQL 不会因为你代码里写了
if (balance 就自动回滚。它只在极少数系统级异常时介入: - 死锁 → InnoDB 主动选一个事务 <code>ROLLBACK- 锁等待超时(
innodb_lock_wait_timeout)→ 默认只回滚最后一条语句;设
innodb_rollback_on_timeout=ON才回滚整个事务(但慎开,可能破坏业务语义)
真要保业务一致性,得靠存储过程里的错误处理器:
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; RESIGNAL; END;
否则,靠人眼判断再敲
ROLLBACK,永远有窗口期。
回滚不是后悔药,它是依赖引擎、连接状态和操作顺序的精密协作。最常被忽略的,其实是
autocommit和
@@in_transaction这两个变量——它们不报错,但能让所有
ROLLBACK归零。
