ExecutorService可调用线程mybatis插入1M+记录抛出CannotGetJdbcConnectionException获取JDBC连接失败

问题描述 投票:0回答:1

以下是 Hikari 连接设置

spring.datasource.hikari.connection-timeout=600000
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.minimum-idle=5

下面是代码,分配线程池大小与最大连接数相同,将1M+记录列表分割10k并插入到数据库,使用mybatis批处理会话

ExecutorService executorService = Executors.newFixedThreadPool(15);
List<List<Employee>> listOfEmployees = new ArrayList<>(IntStream.range(0, totalEmployeeList.size()).boxed().collect(
Collectors.groupingBy(e -> e / 10000, Collectors.mapping(totalEmployeeList::get, Collectors.toList()))).values());
List<Callable<Void>> callables = listOfEmployees.stream().map(sublist ->
(Callable<Void>) () -> {
     dao.insertEmployees(sublist);
      return null;
    }).collect(Collectors.toList());
try {
   executorService.invokeAll(callables);
   executorService.shutdown();
} catch (InterruptedException e) {
  log.error("Exception in executing thread save", e);
}

批量会话代码 配置:

@Bean(value = "batchSqlSession")
@Autowired
public SqlSessionTemplate batchSqlSession(SqlSessionFactory sqlSessionFactory) {       
   return new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH);
}

道:

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) {
        employeeList.stream().parallel().forEach(employee-> {
            try{
                employeeMapper.insertEmployees(employee);
            } catch(Exception ex){
                log.error("Exception - {} while inserting data {}",ex,employee.toString());
            }
        });
        sqlSessionTemplate.flushStatements();
        sqlSessionTemplate.clearCache();
    }

当我运行这个时,我得到

由:org.apache.ibatis.exceptions.PersistenceException引起:

更新数据库时出错。 原因:org.springframework.jdbc.CannotGetJdbcConnectionException:失败

获取 JDBC 连接

错误可能存在于mapper/EmployeeMapper.xml中

错误可能涉及 com.fmr.qrit.datasync.mapper.EmployeeMapper.insertEmployees

执行更新时发生错误

原因:org.springframework.jdbc.CannotGetJdbcConnectionException:无法在

获取 JDBC 连接

org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:84) 在 org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:80) 在 org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:67) 在 org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:348) 在 org.apache.ibatis.executor.BatchExecutor.doUpdate(BatchExecutor.java:70) 在 org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117) 在 org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76) 在 org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:197) ... 22 更多 引起原因:java.sql.SQLTransientConnectionException: HikariPool-1 - 连接不可用,请求超时 30000ms(总计 = 10,活动 = 10,空闲 = 0,等待 = 6) com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:686) 在 com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:179) 在 com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:144) 在 com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:127) 在 org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource.getConnection(AbstractRoutingDataSource.java:213) 在 org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:160) 在 org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:118) 在 org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:81) ... 29 更多

我尝试将连接池大小从 10 增加到 15,然后将连接超时从 300000 增加到 600000,但同样的错误

spring-boot mybatis java-17 oracle19c
1个回答
0
投票

我认为问题在于DAO方法中的employeeList.stream().parallel().forEach()语句。在内部,它拆分列表并并发执行,最终连接结果。这可能会导致过多的并发数据库连接请求,从而导致问题。

并行流非常适合 CPU 密集型操作,但不建议用于 I/O 密集型操作,例如数据库交互。

请尝试使用简单的 employeeList.stream().forEach 操作

© www.soinside.com 2019 - 2024. All rights reserved.