bigywater.tech
Spring · 36

嵌套事务与 Savepoint

NESTED 嵌套事务在数据库里到底是什么?它比普通加入外层事务贵多少?为什么用 JPA 的时候 NESTED 会直接报错?这篇在 Spring Framework 7.0.9、Hibernate 和 MySQL 26.7.0 上把语句和报错逐条量出来。

1 个 COMMIT
NESTED 的 inner 成功或失败时,整个过程在 MySQL 上都只有一个事务、一个 COMMIT
约 2 倍多
一个事务里调用内层方法,NESTED 相比 REQUIRED 每次的耗时(多出 SAVEPOINT 和 RELEASE 两条语句)
JpaDialect
用 JPA / Hibernate 时即使打开 nestedTransactionAllowed,NESTED 仍报“JpaDialect does not support savepoints”

就是 Savepoint

实测 · 本机执行输出
  inner 成功:提交后表里的行 outer-1+inner+outer-2;那个连接上的语句:
    SET autocommit=0
    insert into t(v) values ('outer-1')
    SAVEPOINT `SAVEPOINT_1`
    insert into t(v) values ('inner')
    RELEASE SAVEPOINT `SAVEPOINT_1`
    insert into t(v) values ('outer-2')
    COMMIT
    SET autocommit=1
  inner 失败:提交后表里的行 outer-1+outer-2;那个连接上的语句:
    SET autocommit=0
    insert into t(v) values ('outer-1')
    SAVEPOINT `SAVEPOINT_1`
    insert into t(v) values ('inner')
    ROLLBACK TO SAVEPOINT `SAVEPOINT_1`
    RELEASE SAVEPOINT `SAVEPOINT_1`
    insert into t(v) values ('outer-2')
    COMMIT
    SET autocommit=1

NESTED 在同一个连接上用 SAVEPOINT 和 ROLLBACK TO SAVEPOINT 实现。

代价

实测 · 本机执行输出
  一个事务里调用内层方法 2000 次:REQUIRED 132 ms(每次 66 µs);NESTED 358 ms(每次 179 µs)
  一个事务里调用内层方法 2000 次:REQUIRED 269 ms(每次 135 µs);NESTED 791 ms(每次 396 µs)
  一个事务里调用内层方法 2000 次:REQUIRED 128 ms(每次 64 µs);NESTED 300 ms(每次 150 µs)

每次多两条语句,耗时是 REQUIRED 的两倍多。

JPA 不行

实测 · 本机执行输出
  JpaTransactionManager(setNestedTransactionAllowed(false))+ Hibernate 里调用 NESTED 方法 → NestedTransactionNotSupportedException: Transaction manager does not allow nested transactions by default - specify 'nestedTransactionAllowed' property with value 'true'
  JpaTransactionManager(setNestedTransactionAllowed(true))+ Hibernate 里调用 NESTED 方法 → NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities

JpaTransactionManager 默认不允许,打开后 Hibernate 的 JpaDialect 也不支持 savepoint。

分步演示

嵌套事务与 Savepoint
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用不带 Boot 的 Spring Framework 7.0.9 + DataSourceTransactionManager + HikariCP + 自己起的 MySQL 26.7.0 + Hibernate(Boot 4.0.8 的 BOM);第二节的耗时每次运行抖动大(中间那次 REQUIRED 269 ms、NESTED 791 ms),只能当量级参考;general_log 开在这个实例上
    • 没有验证:Savepoint 在 InnoDB 里对行锁的释放行为;PostgreSQL 等其它数据库;JTA 下的 NESTED;JPA 提供商换成 EclipseLink 时的支持;大量嵌套深度(1000 层)下 savepoint 的资源开销
    京ICP备2026067502号-1京公网安备11010502063487号