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

事务必须显式开启,别指望自动开始
MySQL 默认是自动提交(AUTOCOMMIT=1),也就是说每条 INSERT、UPDATE、DELETE 都会立刻写入磁盘,根本没机会 ROLLBACK。想用事务,第一步就是关掉它——或者更稳妥地,明确用命令开启:
-
BEGIN、BEGIN WORK、START TRANSACTION三者等价,任选其一即可(推荐用START TRANSACTION,语义最清晰) - 千万别只写
COMMIT或ROLLBACK就跑,如果之前没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 exist或ERROR 1205 (40001): Deadlock found - 不能只回滚部分语句——没有“回滚上一条 UPDATE”的语法;要么全退,要么提前用
SAVEPOINT打点:SAVEPOINT sp1→ 做点事 →ROLLBACK TO sp1 - 在应用程序里(比如 Python 的
pymysql或 C# 的SqlTransaction),必须确保Rollback()在try/catch的finally或异常分支里调用,否则连接关闭时事务可能卡在 pending 状态
真实场景下的典型误用
很多人翻文档抄例子,却栽在和业务逻辑耦合的地方:
- 在 Web API 中,一个请求开了事务,但响应返回前忘了
COMMIT或ROLLBACK,连接归还连接池后事务仍挂着,导致表锁、复制延迟、甚至主从不一致 - 用 ORM(如 SQLAlchemy、Django ORM)时,以为
.save()自动进事务,其实默认仍是 auto-commit 模式;要启用事务得显式加@transaction.atomic或session.begin() - 在 MySQL 5.7+ 上用
READ-COMMITTED隔离级别时,SELECT不加锁,但UPDATE仍会加行锁;如果ROLLBACK前已有其他事务读到未提交数据(靠一致性读),它看到的仍是旧值——这不是 bug,是 MVCC 的正常行为
事务不是开关,是状态机;BEGIN 是起点,COMMIT/ROLLBACK 是终点,中间任何环节断掉,都得靠你代码里的兜底逻辑去感知和处理。










