postgresql 不支持嵌套事务,仅通过 savepoint 实现局部回滚;每个 savepoint 是当前事务内的锚点,命名需唯一,rollback to savepoint 可撤销后续操作并继续执行,而 commit 或 rollback 会使所有 savepoint 失效。

嵌套事务在 PostgreSQL 里根本不存在
PostgreSQL 不支持真正的嵌套事务,所谓“嵌套”只是语法糖,BEGIN 后再写 BEGIN 实际上会被忽略,不会创建新事务边界。真正能实现局部回滚的,只有 SAVEPOINT。
常见错误现象:ERROR: invalid transaction termination 或回滚后整个事务意外终止,往往是因为误以为 COMMIT 或 ROLLBACK 能作用于某一层“子事务”。
-
SAVEPOINT是唯一可控的中间标记点,它不开启新事务,只在当前事务内设锚点 - 每个
SAVEPOINT必须有唯一名字,重复定义会覆盖前一个(不是报错) -
RELEASE SAVEPOINT只是释放标记,不影响数据;ROLLBACK TO SAVEPOINT才真正撤销之后的操作 - 一旦执行
COMMIT或ROLLBACK,所有SAVEPOINT全部失效
MySQL 的 START TRANSACTION 和 SAVEPOINT 行为差异
MySQL 支持 START TRANSACTION,但它和 BEGIN 完全等价,依然不构成嵌套。关键区别在于:MySQL 在存储过程中允许用 DECLARE EXIT HANDLER 捕获异常并 ROLLBACK TO SAVEPOINT,而外部客户端不能靠事务语句自动恢复。
使用场景:在存储过程里做可选操作(比如尝试插入用户,失败则退回到插入前状态,继续发通知)。
破浪分红权返利系统是在破浪直销系统的基础上独立自主开发的一套稳定完善的购物商场网站管理系统,系统基于PHP+MYSQL开发,集购物商城、积分商城、商家联盟、会员营销机制等一体,模板与程序分离,集成网上支付,嵌入型短信应用API集成,使用简单、功能强大,多种返现模式可自由选择,为广大创业者者提供一个快速、高效、稳定、安全的电子商务系统。系统集O2O\C2C\B2C\B2B2C以及直销、分红、代理、分
- MySQL 5.7+ 对
SAVEPOINT名字大小写不敏感,但建议统一小写避免混淆 - 如果在
INSERT ... ON DUPLICATE KEY UPDATE后设SAVEPOINT,回滚不会影响该语句已生效的更新 - MyISAM 引擎不支持事务,任何
SAVEPOINT都静默忽略,务必确认引擎类型 - 并发下多个连接用同名
SAVEPOINT不冲突,因为作用域仅限本连接当前事务
Python 中用 psycopg2 控制 SAVEPOINT 的典型写法
psycopg2 不提供自动 savepoint 管理,必须显式调用 cursor.execute("SAVEPOINT sp1")。直接用 connection.rollback() 会回滚整个事务,不是局部。
容易踩的坑:在 try/except 里调用 connection.rollback() 而不是 cursor.execute("ROLLBACK TO SAVEPOINT sp1"),导致前面所有操作丢失。
- savepoint 名字推荐用
f"sp_{int(time.time())}_{random.randint(100,999)}"这类生成方式,避免硬编码重名 - 不要在循环里无条件建同名
SAVEPOINT,否则后一次会覆盖前一次,导致无法回滚到预期位置 - psycopg2 的
connection.autocommit = True模式下,SAVEPOINT无效,会报ProgrammingError: SAVEPOINT can only be used in transaction blocks - 用
with connection.cursor() as cur:不代表自动管理 savepoint,仍需手动配对SAVEPOINT/ROLLBACK TO
ROLLBACK TO SAVEPOINT 后还能 INSERT 吗
可以,而且这是它的设计目的:回滚到某个点之后,事务仍处于活跃状态,后续 SQL 继续执行、最终 COMMIT 仍有效。
性能影响很小,因为 SAVEPOINT 本质只是记录 WAL 中的一个位置指针,不复制数据或锁表;但频繁建/回滚大量 savepoint 会略微增加 WAL 体积和解析开销。
- PostgreSQL 12+ 支持
RELEASE SAVEPOINT显式清理,但非必需;事务结束时自动清理所有 savepoint - 如果
ROLLBACK TO SAVEPOINT后又执行了 DDL(如CREATE TABLE),该 DDL 也会被回滚——DDL 在 PostgreSQL 中强制隐式提交,所以实际不能出现在 savepoint 之后 - 某些 ORM(如 SQLAlchemy)的
session.begin_nested()底层就是SAVEPOINT,但要注意它默认用固定名字sa_savepoint,多层嵌套可能相互覆盖









