mysql事务回滚异常怎么办_mysql一致性问题排查

来源:这里教程网 时间:2026-02-28 20:44:23 作者:

事务回滚不生效,先确认是否在自动提交模式下

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

相关推荐