事务回滚不生效的主因是autocommit=1导致每条SQL独立提交,需SET autocommit=0或START TRANSACTION显式开启事务;同时需排查存储引擎、binlog格式及隔离级别影响。

事务回滚不生效,先确认是否在自动提交模式下
MySQL 默认开启 autocommit=1,此时每条 SQL 都是独立事务,ROLLBACK 对它无效——因为事务早就提交了。常见现象是执行了 BEGIN 和几条 UPDATE,再 ROLLBACK 却发现数据已改,根本没回滚。
- 用
SELECT @@autocommit;查当前会话状态,返回1就说明处于自动提交模式 - 临时关闭:执行
SET autocommit = 0;(注意:该设置只对当前会话有效) - 更稳妥的做法是显式用
START TRANSACTION或BEGIN开启事务,而非依赖 autocommit 切换 - 某些 ORM(如 Django 默认、SQLAlchemy 的
autocommit=False模式)会接管事务控制,需检查其配置是否意外触发了隐式提交
回滚失败报错 “ERROR 1305 (42000): SAVEPOINT does not exist”
这个错误通常出现在嵌套事务或多次 SAVEPOINT 操作中,本质是试图回滚一个已被释放或从未建立的保存点。MySQL 不支持真正的嵌套事务,SAVEPOINT 只是事务内的标记,一旦上层 ROLLBACK TO SAVEPOINT 执行后,该保存点之后创建的其他保存点会自动失效。
- 避免重复定义同名保存点:
SAVEPOINT sp1;后再次执行同名语句不会报错,但会覆盖前一个,容易误判 - 回滚后不要再用已被回滚的保存点:
ROLLBACK TO SAVEPOINT sp1;之后,RELEASE SAVEPOINT sp1;仍可执行,但再次ROLLBACK TO SAVEPOINT sp1;就会报错 - 调试时可用
SELECT @@in_transaction;确认是否仍在事务中;值为1表示事务活跃,0表示已提交或回滚完毕
主从不一致时,事务回滚在从库“看起来没发生”
MySQL 主从复制基于 binlog,而回滚操作本身不写 binlog——也就是说,ROLLBACK 不会同步到从库。这本身不是 bug,但若你在主库回滚前已发生部分写入(比如跨库操作、UDF、MyISAM 表修改),或 binlog 格式为 STATEMENT 且语句含不确定性函数(NOW()、RAND()),就可能造成主从数据分叉。
- 检查 binlog 格式:
SELECT @@binlog_format;,生产环境强烈建议设为ROW - 确认回滚前是否有非事务引擎操作:MyISAM 表的变更无法回滚,且会记录进 binlog,导致从库执行成功而主库回滚,结果不一致
- 用
SHOW BINLOG EVENTS IN 'mysql-bin.000001' FROM 1234 LIMIT 10;查看对应事务在 binlog 中是否只包含BEGIN和实际 DML,不含ROLLBACK - 如果已在从库发现不一致,不要直接
STOP SLAVE; SET GLOBAL sql_slave_skip_counter = 1;跳过,应先比对主从表校验和(如pt-table-checksum)定位范围
一致性校验发现幻读/不可重复读,不是回滚问题而是隔离级别所致
事务回滚异常常被误认为“数据不一致”,但很多情况其实是隔离级别(isolation level)导致的正常行为。例如在 REPEATABLE READ 下两次 SELECT 返回不同结果,不是回滚失败,而是 MVCC 机制下快照读与当前读混用的结果。
- 确认当前事务隔离级别:
SELECT @@transaction_isolation;(MySQL 8.0+)或SELECT @@tx_isolation;(旧版本) -
READ COMMITTED下每次SELECT都会新建一致性视图,可能看到其他事务已提交的变更;REPEATABLE READ下首次SELECT建立视图,后续相同查询结果不变——但SELECT ... FOR UPDATE或INSERT ... SELECT会触发当前读,绕过快照 - 幻读在
REPEATABLE READ下仍可能发生(如INSERT新行后SELECT ... WHERE未命中),需靠间隙锁(gap lock)或升级到SERIALIZABLE(但性能代价大) - 排查时优先用
SELECT * FROM performance_schema.data_locks;(需开启performance_schema)查看事务持有的锁类型和范围
SELECT ENGINE_TRANSACTION_ID AS trx_id, OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE, LOCK_DATA FROM performance_schema.data_locks WHERE ENGINE_TRANSACTION_ID IN ( SELECT ID FROM performance_schema.data_lock_waits );
事务回滚本身逻辑简单,但它的表现高度依赖 autocommit 设置、存储引擎特性、复制机制和隔离级别。最容易被忽略的是:你以为在事务里,其实早已提交;你以为回滚了数据,其实只是没影响到从库;你以为不一致是回滚失败,其实是 MVCC 正常工作。查问题前,先确认这四件事——@@autocommit、@@engine(InnoDB 还是 MyISAM)、@@binlog_format、@@transaction_isolation。










