已提交的delete或update无法用rollback撤销,因autocommit=1下语句执行即永久生效;补救需依赖binlog解析、备份恢复或软删除机制。

事务已提交,ROLLBACK 无效怎么办
多数人卡在这一步:执行完 UPDATE 或 DELETE 后发现错了,立刻敲 ROLLBACK,结果提示 There is no transaction to rollback。这不是命令写错了,而是事务早已结束——MySQL 默认 autocommit=1,每条语句都是独立事务,执行完即提交。
验证方式很简单:SELECT @@autocommit; 返回 1 就说明你从没真正“开启事务”。此时 ROLLBACK 不是失败,是压根没东西可滚。
- 临时补救:下一次操作前先执行
SET autocommit = 0;,再START TRANSACTION;,所有 DML 包进里面 - 别指望连接断开后还能回滚——客户端崩溃、超时、被 kill,未提交事务直接丢弃,不是回滚
- DDL(如
ALTER TABLE)会强制提交当前事务,之后的ROLLBACK只影响它之后的 DML
Yii::app()->db->beginTransaction() 没生效的常见原因
Yii 中手动开启事务看似简单,但实际失效频率很高。最典型的是:事务对象没被正确传递到后续查询,或中间混入了非事务安全的操作。
- AR 查询(如
User::find()->one())默认不参与当前事务上下文,除非显式指定->asArray()->createCommand()或用 DAO - 事务内调用了外部 API、文件写入、或未捕获异常的
try/catch,导致rollback()没被执行,连接却还开着——这时事务状态残留,但数据已部分提交 - 多个模型操作共用一个事务时,某个模型的
save()失败但没抛异常(比如规则校验失败返回false),后续仍继续执行,最终commit()把部分变更落库 - 事务嵌套不被 InnoDB 支持,Yii 的
beginTransaction()嵌套调用只会静默忽略,不会报错
已提交误操作,靠什么还原数据
别碰 undo log。它不是备份,不能导出,也没有公开解析接口。MySQL 8.0 的 innodb_undo_001 是内部页结构,硬解析大概率损坏表空间。
真正可用的路径只有三条,按优先级排列:
- 开启
binlog_format=ROW的前提下,用mysqlbinlog --base64-output=DECODE-ROWS -v解析对应时间段 binlog,把DELETE转成INSERT,把UPDATE的@1/@2字段值还原成旧值再拼 SQL - 有全量备份(如
mysqldump或xtrabackup)+ binlog,就恢复备份,再重放 binlog 到误操作前一秒;GTID 模式下需SET GTID_NEXT跳过错误事件 - 无 binlog、无备份?检查是否启用了应用层软删除(如
deleted_at字段)。如果是,直接查onlyTrashed()并调用restore(),这是唯一零风险还原路径
Yii 模型里 isNewRecord 和事务回滚的陷阱
事务回滚后,AR 实例的 isNewRecord 状态不会自动同步。比如你 new User() → save() → 回滚,数据库没数据,但实例的 isNewRecord 仍是 false。下次再 save(),可能触发主键冲突或覆盖逻辑错误。
- 判断真实存在性,必须查库:
User::findOne($id) !== null,不能只信isNewRecord - 事务中创建模型后又回滚,建议手动重置:
$model->setIsNewRecord(true),否则后续行为不可控 - 批量操作(如循环
save())遇到部分失败时,不要依赖事务自动回滚——AR 的save()默认不抛异常,得手动if (!$model->save()) { $transaction->rollback(); break; }
最难的从来不是写对那行 rollback(),而是确认“此刻数据库到底处于什么状态”。只要表里有业务数据、外键、视图或触发器依赖某字段,任何 down() 或 DROP COLUMN 都不再是代码问题,而是数据契约的断裂。











