java应用Out of Memory的情况下,spring事务会回滚嘛?

本文通过一个示例分析了JVM内存溢出问题,配置了连接池和事务回滚策略。在测试代码中,由于大量对象创建导致内存溢出,而日志显示事务在出现异常后正常回滚。尽管只配置了回滚Exception,但Error也会触发回滚,这是由于Spring的默认回滚策略决定的。通过对源码的查看,确认了即使未命中自定义回滚规则,父类的回滚策略仍会处理Error。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   

1.设置JVM大小:-Xms32m -Xmx32m

2.连接池配置:

    poolName: TEST_Mysql_Hikari_Pool
    minimumIdle: 10
    maximumPoolSize: 1000
    maxLifetime: 900000
    connectionTimeout: 6000
    idleTimeout: 300000
    leakDetectionThreshold: 15000

3.新增测试代码

@Transactional(rollbackFor = Exception.class)
    @RequestMapping("/testOutOfMemoryAndTransactionRollBack")
    public void testRefineClass() throws Exception{    
        System.out.println("a211c");
        myFileMapper.deleteFile("xiaxh1", 5900l);    
        List<MyBean> files=new ArrayList<MyBean>();
        int i=0;
        while(true) {
            MyBean f=new MyBean();
            f.setDocName(UUID.randomUUID().toString());
            files.add(f);
            if(i++>1000000000) {
                break;
            }
        }
        System.out.println(files.size());
    }

3.访问/testOutOfMemoryAndTransactionRollBack

4.观察日志

日志1:

[WARN ][TEST_Mysql_Hikari_Pool housekeeper] com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detection triggered for com.mysql.jdbc.JDBC4Connection@25d1ebee on thread http-nio-17000-exec-1, stack trace follows
java.lang.Exception: Apparent connection leak detected
    at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:128)

日志解析: leakDetectionThreshold: 15000 设置了连接被从连接池取出来之后15秒仍然未归还到连接池,则会打印次信息,注意线程是:TEST_Mysql_Hikari_Pool housekeeper,并不是用户请求处理线程:http-nio-17000-exec-*,所以这只是提示信息,并不影响用户线程的执行。

日志2:

[http-nio-17000-exec-1] org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@31dbe98f]

[2021-07-24 13:39:26.335][DEBUG][http-nio-17000-exec-1] org.mybatis.spring.SqlSessionUtils - Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@31dbe98f]
[2021-07-24 13:39:26.336][DEBUG][http-nio-17000-exec-1] org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@31dbe98f]

日志解析:事务管理器关闭SqlSession

日志3:

[2021-07-24 13:39:26.358][INFO ][http-nio-17000-exec-1] com.zaxxer.hikari.pool.ProxyLeakTask - Previously reported leaked connection com.mysql.jdbc.JDBC4Connection@25d1ebee on thread http-nio-17000-exec-1 was returned to the pool (unleaked)

日志解析:跟日志1相互呼应的,用户请求处理线程在事务处理完毕之后归还数据库连接到连接池,但是这个连接之前已经被报告过借用连接超时。

日志4:
[2021-07-24 13:39:26.395][ERROR][http-nio-17000-exec-1] org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: Java heap space] with root cause
java.lang.OutOfMemoryError: Java heap space

日志解析:用户请求处理线程抛出OutOfMemoryError异常

查看DB数据库记录,未更新成功

疑问:@Transactional(rollbackFor = Exception.class) 写明的是回滚Exception,为啥Error也回滚了呢?

翻看源码:

类1:DefaultTransactionAttribute

@Override
    public boolean rollbackOn(Throwable ex) {
        return (ex instanceof RuntimeException || ex instanceof Error);
    }

类2:RuleBasedTransactionAttribute extends DefaultTransactionAttribute

@Override
    public boolean rollbackOn(Throwable ex) {
        if (logger.isTraceEnabled()) {
            logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
        }

        RollbackRuleAttribute winner = null;
        int deepest = Integer.MAX_VALUE;

        if (this.rollbackRules != null) {
            for (RollbackRuleAttribute rule : this.rollbackRules) {
                int depth = rule.getDepth(ex);
                if (depth >= 0 && depth < deepest) {
                    deepest = depth;
                    winner = rule;
                }
            }
        }

        if (logger.isTraceEnabled()) {
            logger.trace("Winning rollback rule is: " + winner);
        }

        // User superclass behavior (rollback on unchecked) if no rule matches.
        if (winner == null) {
            logger.trace("No relevant rollback rule found: applying default rules");
            return super.rollbackOn(ex);
        }

        return !(winner instanceof NoRollbackRuleAttribute);
    }

debug下看到的

 如果配置了规则,则按照规则走,如果找不到,依然会调用父类的来判断是否回滚:super.rollbackOn(ex);

这样就很清晰了,虽然配置了回滚规则,如果未命中回滚规则,则父类的回滚规则: return (ex instanceof RuntimeException || ex instanceof Error);依然会执行。

<think>嗯,用户问的是ShardingSphere框架会不会影响Spring事务回滚。这个问题需要仔细分析。首先,我应该回顾一下ShardingSphere的基本功能,它是一个分库分表的中间件,处理数据分片、读写分离等。而Spring事务管理主要是通过AOP实现的,特别是声明式事务,用@Transactional注解来控制。 接下来,得考虑ShardingSphere如何处理数据库连接。因为分片可能需要多个数据源,所以ShardingSphere会自己管理这些连接。这时候,Spring事务管理器是否能正确协调这些连接是关键。如果ShardingSphere的数据源被Spring事务管理器包装,比如用DataSourceTransactionManager,那么事务的提交和回滚应该由Spring处理。但如果ShardingSphere用的是分布式事务管理器,比如XA,可能需要不同的处理方式。 然后,想到分布式事务的情况。如果用户配置了XA事务,ShardingSphere可能会覆盖Spring事务管理,这时候回滚机制可能由ShardingSphere自己控制,需要确认两者是否兼容。另外,异常捕获方面,Spring事务回滚依赖于检查到异常,如果ShardingSphere在操作中吞掉了异常,或者抛出的异常不是Spring预期的,可能会导致回滚失败。 还有,分库分表的事务问题。跨库操作需要分布式事务支持,如果ShardingSphere没有正确配置,可能导致部分成功部分失败,这时候Spring事务管理器可能无法正确回滚所有操作。例如,本地事务可能只能回滚当前连接的操作,而其他分片的事务可能没有被管理到。 需要确认用户的使用场景,是否开启了分布式事务,数据源配置是否正确,以及是否有异常处理不当的情况。常见的解决方案包括检查配置,确保使用支持分布式事务的管理器,处理异常正确,以及在需要时手动回滚事务。 可能用户在实际使用中遇到了事务没有按预期回滚的情况,需要根据具体配置和环境来分析。比如,如果用的是本地事务,跨库操作无法保证原子性,这时候ShardingSphere可能会影响Spring事务回滚。而如果正确配置了分布式事务,两者应该可以协同工作。</think>ShardingSphere 框架是否会影响 Spring事务回滚,需要从 **事务管理机制** 和 **ShardingSphere 的分片特性** 两个层面逐步分析: --- ### 一、基础原理 1. **Spring 事务机制** Spring 通过 `PlatformTransactionManager` 实现事务管理(如 `DataSourceTransactionManager`),其核心是通过 `@Transactional` 注解控制事务边界,依赖数据库连接的提交(commit)和回滚(rollback)。 2. **ShardingSphere 的核心功能** ShardingSphere 作为分库分表中间件,会通过代理(如 `ShardingSphereDataSource`)对 SQL 进行解析、路由和改写,可能涉及多个物理数据库连接。 --- ### 二、潜在影响场景 #### 场景 1:单数据源分片 - **无分布式事务时** 若仅使用 **本地事务**(如单库分表),ShardingSphere 会将多个操作绑定到同一个数据库连接,Spring事务管理器仍能正常回滚。 - **示例代码** ```java @Transactional public void insertData() { userRepository.insert(user1); // 分片到同一个库的不同表 orderRepository.insert(order1); } ``` 此时若发生异常,Spring 可通过回滚当前连接实现事务一致性。 #### 场景 2:跨库操作 - **分布式事务需求** 若操作涉及 **多个物理数据库**(如分库),需显式启用分布式事务(如 XA 或 Seata)。否则: - **本地事务无法跨库回滚** 例如:操作 A 在 DB1 提交成功,操作 B 在 DB2 失败,DB1 的数据无法自动回滚。 - **关键配置** ```yaml # 启用 XA 事务 spring.shardingsphere.rules.sharding.default-database-strategy.xxx spring.shardingsphere.props.xa-transaction-manager-type: Atomikos ``` --- ### 三、常见问题与解决方案 #### 问题 1:事务未按预期回滚 - **可能原因** - ShardingSphere 未正确配置分布式事务管理器 - 异常未被 Spring 捕获(如非 `RuntimeException`) - 分片键路由导致跨库操作未启用分布式事务 - **解决方案** 1. 检查是否添加分布式事务依赖(如 `shardingsphere-transaction-xa-core`) 2. 确保异常类型匹配 `@Transactional(rollbackFor = Exception.class)` 3. 通过 `@ShardingTransactionType(TransactionType.XA)` 显式标记分布式事务 #### 问题 2:性能下降 - **原因** XA 事务的 **两阶段提交协议** 会引入额外开销。 - **优化建议** - 优先使用 **BASE 事务**(如 Seata 的 AT 模式) - 缩小事务粒度,避免跨过多分片 --- ### 四、验证步骤 1. **基础验证** ```java @Transactional public void testRollback() { insertShardedData(); // 单库分表操作 throw new RuntimeException("Force rollback"); } ``` 观察数据是否回滚。 2. **跨库验证** ```java @ShardingTransactionType(TransactionType.XA) @Transactional public void testDistributedRollback() { insertDB1(); // 分库操作 insertDB2(); throw new RuntimeException("Force rollback"); } ``` 检查多库数据一致性。 --- ### 五、总结 | 场景 | 是否影响 Spring 回滚 | 关键条件 | |------|----------------------|----------| | 单库分表 | 否 | 使用本地事务 | | 多库分片 | 是 | 未启用分布式事务 | | 启用 XA/Seata | 否 | 正确配置分布式事务 | **结论**:ShardingSphere **本身不会破坏 Spring 事务机制**,但需根据分片场景选择合适的事务模型。跨库操作必须显式启用分布式事务,否则会导致部分提交问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值