You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ExecutorService批量插入百万条数据时触发CannotGetJdbcConnectionException

解决ExecutorService+MyBatis批量插入时的CannotGetJdbcConnectionException异常

问题根源分析

  • 线程数爆炸导致连接池耗尽:外部已使用15线程的固定线程池,DAO层又通过parallel().forEach开启多线程,瞬间产生远超过连接池容量的并发连接请求,直接耗尽所有连接。
  • 事务注解误用:@Transactional添加在insertEmployees方法上,结合并行流会导致每个子线程都尝试获取新的事务连接,进一步加剧连接争抢;同时事务会持有连接直到方法结束,延长连接占用时间。
  • MyBatis BATCH执行器未正确利用:循环单条插入而非调用批量插入方法,BATCH执行器的批量提交优势未发挥,反而增加了连接的持有时间和操作次数。

解决方案

1. 移除嵌套并行流,改用MyBatis批量插入

修改DAO层代码,删除parallel().forEach,直接调用Mapper的批量插入方法,减少连接占用时间和并发请求数:

public class EmployeeDAO {
    private final SqlSessionTemplate sqlSessionTemplate;
    private final EmployeeMapper employeeMapper;

    public EmployeeDAO(EmployeeMapper employeeMapper,
                       @Qualifier("batchSqlSession") SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
        this.employeeMapper = sqlSessionTemplate.getMapper(EmployeeMapper.class);
    }

    // 移除@Transactional,避免事务绑定连接导致长时间占用
    public void insertEmployees(List<Employee> employeeList) {
        try {
            // 调用批量插入Mapper方法
            employeeMapper.batchInsertEmployees(employeeList);
            sqlSessionTemplate.flushStatements();
            sqlSessionTemplate.clearCache();
        } catch(Exception ex) {
            log.error("Exception while inserting batch data", ex);
        }
    }
}

对应的Mapper接口增加批量插入方法:

public interface EmployeeMapper {
    void batchInsertEmployees(@Param("list") List<Employee> employeeList);
}

Mapper XML编写批量插入语句:

<insert id="batchInsertEmployees">
    INSERT INTO employee (id, name, ...)
    VALUES
    <foreach collection="list" item="emp" separator=",">
        (#{emp.id}, #{emp.name}, ...)
    </foreach>
</insert>

2. 调整线程池与连接池的匹配策略

外部线程池的大小建议不要超过连接池最大连接数,同时预留少量连接应对突发情况,比如将线程池大小设为12(连接池max为15):

ExecutorService executorService = Executors.newFixedThreadPool(12);

3. 修正事务使用方式

如果需要全局事务控制,在调用线程池的上层方法添加@Transactional,而非在每个子任务方法中添加;如果不需要事务,直接移除@Transactional,让批量SqlSession自行管理连接。

4. 优化Hikari连接池配置,排查连接泄漏

开启Hikari的连接泄漏检测,定位是否有连接未被正确释放:

spring.datasource.hikari.leak-detection-threshold=20000 # 20秒检测泄漏

同时确保SqlSessionTemplate的配置正确,Spring会自动管理其连接的释放,避免手动操作不当导致泄漏。

验证效果

修改后,每个线程仅持有连接完成一次批量插入操作,连接占用时间大幅缩短,并发请求数控制在连接池容量范围内,即可解决无法获取JDBC连接的问题。

内容的提问来源于stack exchange,提问作者Pat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 21:29:51