mysql数据库中的事务回滚与提交机制

4次阅读

rollback仅撤回当前未提交事务中的insert、update、delete操作,不回滚ddl、select、set等;在autocommit=1或隐式提交后无效,且受savepoint范围限制。

mysql数据库中的事务回滚与提交机制

事务回滚(ROLLBACK)到底撤回哪些操作

回滚只对当前未提交的事务生效,且仅撤回该事务内执行的 INSERTUPDATEDELETE,不涉及 CREATEDROPALTER 等 DDL 语句(它们会隐式提交)。如果执行了 SELECTSET 变量,也不会被回滚——这些本身就不属于事务性变更。

常见误判场景:

  • 在自动提交模式(autocommit=1)下执行单条 UPDATE 后直接 ROLLBACK,实际无效,因为语句已自动提交
  • 事务中调用了存储过程,而过程内部含 COMMIT,会导致事务提前结束,后续 ROLLBACK 只影响它之后的语句
  • 使用了 SAVEPOINT 后回滚到某点,注意只能回滚到该保存点之后的操作,之前的部分仍保留

COMMIT 的不可逆性与隐式提交风险

COMMIT 一旦成功返回,修改就永久写入磁盘(取决于 innodb_flush_log_at_trx_commit 配置),无法通过数据库命令撤销。但更常被忽略的是“隐式提交”:以下操作会强制结束当前事务并自动提交,即使你没写 COMMIT

  • 执行 DDL 语句(如 CREATE tableALTER TABLE
  • 执行 LOCK TABLESUNLOCK TABLES
  • 执行 START TRANSACTIONBEGIN(会提交前一个事务)
  • 执行 mysql 客户端的 source 命令加载 SQL 文件时,若文件含 DDL,也会触发隐式提交

这意味着,如果你在事务中先 UPDATE,再 CREATE TEMPORARY TABLE,那么 UPDATE 实际已在 CREATE 时提交,后续 ROLLBACK 对它无效。

如何确认当前是否处于事务中、以及 autocommit 状态

不能只靠记忆或代码注释判断事务状态。最可靠的方式是实时查系统变量:

SELECT @@autocommit, @@in_transaction;

其中:

  • @@autocommit = 1 表示默认每条语句自动提交;设为 0 后需显式 COMMIT/ROLLBACK
  • @@in_transaction = 1 表示当前连接有活跃事务(无论 autocommit 是 0 还是 1,只要执行过 BEGIN 或第一条 DML 就可能为 1)

注意:@@in_transaction 在 MySQL 5.7+ 才可用;低版本可用 SELECT TRX_ID FROM INforMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID() 辅助判断,但开销略大。

事务隔离级别对回滚/提交行为的影响

隔离级别不改变 COMMITROLLBACK 的基本语义,但会影响“其他事务能否看到你的未提交变更”,进而影响业务逻辑中的判断依据。例如:

  • READ COMMITTED 下,事务 A 修改某行后未提交,事务 B 查询不到该修改;但如果 A 提交了,B 后续查询立刻可见
  • REPEATABLE READ(InnoDB 默认)下,事务 B 第一次 SELECT 后,即使 A 提交了修改,B 再次 SELECT 仍看到快照数据——这容易让人误以为“A 的 COMMIT 没生效”
  • SELECT ... FOR UPDATELOCK IN SHARE MODE 会加锁,若此时被阻塞,ROLLBACK 是释放锁的唯一方式,否则可能造成死锁或长等待

真正容易出问题的点在于:开发时本地用 READ UNCOMMITTED 测试,上线切到 REPEATABLE READ 后,原本依赖“读到未提交数据”的逻辑突然失效,而错误往往不报错,只表现为空数据或陈旧数据。

text=ZqhQzanResources