SQL BEGIN、COMMIT、ROLLBACK 使用案例

2次阅读

事务必须显式开启,mysql默认自动提交导致无法回滚;应使用start transaction开启,配合commit不可逆落盘或rollback撤销未提交操作,并注意savepoint、连接池和orm中的事务管理陷阱。

SQL BEGIN、COMMIT、ROLLBACK 使用案例

事务必须显式开启,别指望自动开始

MySQL 默认是自动提交(autoCOMMIT=1),也就是说每条 INSERTUPDATEdelete 都会立刻写入磁盘,根本没机会 ROLLBACK。想用事务,第一步就是关掉它——或者更稳妥地,明确用命令开启:

  • BEGINBEGIN WORKSTART TRANSACTION 三者等价,任选其一即可(推荐用 START TRANSACTION,语义最清晰)
  • 千万别只写 COMMITROLLBACK 就跑,如果之前没 START TRANSACTION,MySQL 会报 Error 1305 (42000): SAVEPOINT identifier does not exist 这类误导性错误
  • 在存储过程或函数里调用事务命令,要注意调用限制:中间夹了 select 或函数调用,COMMIT/ROLLBACK 可能直接被忽略(尤其在某些兼容模式下)

COMMIT 不是“保存”,而是“不可逆落盘”

COMMIT 的作用不是把数据“存起来”,而是把事务内所有变更从临时状态刷进物理文件,并释放锁、清空 undo log。一旦成功,就真没了回头路:

  • 执行 COMMIT 后再断电、重启,数据仍在;而没 COMMIT 就崩了,所有修改全丢
  • 不要在循环里频繁 COMMIT(比如逐条插入后都 COMMIT),这会极大拖慢速度——每提交一次都要刷盘、写 binlog、更新 MVCC 版本链
  • 想批量提交又怕内存溢出?可以用 COMMIT AND CHAIN,它提交后自动开启新事务,保持隔离级别不变,比手动写两个 COMMIT + START TRANSACTION 更安全

ROLLBACK 是唯一可靠的“后悔药”,但有前提

ROLLBACK 能撤销的,仅限当前事务中尚未 COMMIT 的所有操作。它不是万能的,也不是随时都能吃:

  • 如果连接已断开、事务超时、或执行过 DDL(如 ALTER table),ROLLBACK 会直接失败,报错类似 ERROR 1305 (42000): SAVEPOINT identifier does not existERROR 1205 (40001): Deadlock found
  • 不能只回滚部分语句——没有“回滚上一条 UPDATE”的语法;要么全退,要么提前用 SAVEPOINT 打点:SAVEPOINT sp1 → 做点事 → ROLLBACK TO sp1
  • 在应用程序里(比如 Python 的 pymysql 或 C# 的 SqlTransaction),必须确保 Rollback()try/catchfinally 或异常分支里调用,否则连接关闭时事务可能卡在 pending 状态

真实场景下的典型误用

很多人翻文档抄例子,却栽在和业务逻辑耦合的地方:

  • 在 Web API 中,一个请求开了事务,但响应返回前忘了 COMMITROLLBACK,连接归还连接池后事务仍挂着,导致表锁、复制延迟、甚至主从不一致
  • 用 ORM(如 SQLAlchemy、django ORM)时,以为 .save() 自动进事务,其实默认仍是 auto-commit 模式;要启用事务得显式加 @transaction.atomicsession.begin()
  • 在 MySQL 5.7+ 上用 READ-COMMITTED 隔离级别时,SELECT 不加锁,但 UPDATE 仍会加行锁;如果 ROLLBACK 前已有其他事务读到未提交数据(靠一致性读),它看到的仍是旧值——这不是 bug,是 MVCC 的正常行为

事务不是开关,是状态机;BEGIN 是起点,COMMIT/ROLLBACK 是终点,中间任何环节断掉,都得靠你代码里的兜底逻辑去感知和处理。

text=ZqhQzanResources