
MySQL 事务中怎么设保存点、回滚到某一点
MySQL 支持 SAVEPOINT,但仅限于支持事务的引擎(如 InnoDB),MyISAM 完全不认这个。设保存点不是为了“分段提交”,而是给 ROLLBACK 多一个落点——你可以回滚到某个 SAVEPOINT,而不影响它之前的操作。
实操上分三步:SAVEPOINT name → 做可能出错的操作 → 出错了就 ROLLBACK TO SAVEPOINT name。
-
SAVEPOINT名字不能重复,同名会覆盖前一个(不会报错,但容易误判) - 保存点只在当前事务内有效,事务一提交或回滚,所有保存点自动失效
- 不能跨连接使用,A 连接设的
sp1,B 连接执行ROLLBACK TO SAVEPOINT sp1直接报错:SAVEPOINT sp1 does not exist
为什么 ROLLBACK TO SAVEPOINT 后还能继续执行 INSERT/UPDATE
因为 ROLLBACK TO SAVEPOINT 只撤销该保存点之后的变更,事务本身没结束,仍处于活跃状态。这和 ROLLBACK 全量回滚有本质区别。
典型场景是批量导入:逐条插数据,某条失败(比如唯一键冲突),就回滚到最后一个成功点,再跳过这条继续。
- 执行
ROLLBACK TO SAVEPOINT sp2后,必须显式RELEASE SAVEPOINT sp2才算清理干净(否则下次再用同名会覆盖,但不释放也不报错) - 保存点本身不占用太多资源,但嵌套太深(比如循环里反复
SAVEPOINT)可能轻微拖慢事务状态管理 - 注意:
RELEASE SAVEPOINT不影响数据,只是删掉这个标记;而ROLLBACK TO是真改数据
常见错误:SAVEPOINT 在存储过程中失效或报错
存储过程里用 SAVEPOINT 很容易翻车,核心原因是 MySQL 的异常处理机制和保存点生命周期不完全对齐。
比如你在 DECLARE EXIT HANDLER 里写 ROLLBACK TO SAVEPOINT sp,但若 sp 是在 handler 外部定义的,且事务已因其他错误部分回滚过,sp 可能已被清除。
- 务必在每个需要保护的逻辑块开头重新声明
SAVEPOINT,别想复用外层的 - 避免在
START TRANSACTION之前设保存点——会报错:SAVEPOINT inside a transaction is not allowed - 如果存储过程里调用了另一个含
COMMIT的过程,当前事务直接终结,所有保存点作废
Python + PyMySQL 中怎么安全用 SAVEPOINT
ORM(如 SQLAlchemy)默认不暴露保存点接口,原生驱动更可控。PyMySQL 允许在同一个 Connection 对象里手动发 SAVEPOINT 指令,但要注意连接不能被自动 commit 干扰。
关键点:必须关掉 autocommit,且所有操作走同一个 cursor 或至少同个 connection。
- 别在不同线程里共享 connection 并各自设
SAVEPOINT,MySQL 会按连接隔离,但 Python 层容易串 - 示例片段:
conn.autocommit = False<br>cursor.execute("SAVEPOINT sp_insert")<br>try:<br> cursor.execute("INSERT INTO t VALUES (1)")<br>except:<br> cursor.execute("ROLLBACK TO SAVEPOINT sp_insert")<br> # 继续其他逻辑 - 忘记关 autocommit 是最常踩的坑:设了
SAVEPOINT,但下一条INSERT自动提交了,保存点就失去意义
保存点看着轻量,实际依赖事务的完整生命周期管理。最容易被忽略的是:它不解决并发冲突,也不替代应用层的幂等设计——你回滚了,但上游可能已经发了两次请求。











