自己写了一个JDBC连接池去做批量导入,每次运行500万数据,但是在中间就会出各种问题
问题1:The last packet successfully received from the server was 18,967 milliseconds ago. The last packet sent successfully to the server was 18,963 milliseconds ago.
对于问题一 这个时间默认是8小时,我刚起项目,运行不到30分钟,怎么会连接过期呢?网上说的去改哪个out时间,我也改过无效?单是看这个时间觉得也不是网上说的那样?
问题2:截了个图可以看图片
代码也贴上了,每次报错都是提交的时候报的错误,ps.executeBatch();;
```java
private void saveForecastSales(List<ForecastSalesDO> forecastSalesDOS, int count) {
try {
if (count > 2) {
log.info("JDBCOperationServiceImpl->saveForecastSales:: 尝试两次无果");
return;
}
Connection connection = dbPool.getConnection();
if (!connection.isValid(1000)){
dbPool.removeConnection(connection);
log.error("JDBCOperationServiceImpl->saveForecastSales::connection ");
connection = DbUtil.createConnection();
}
PreparedStatement ps = connection.prepareStatement(JDBCUtils.SQL);
connection.setAutoCommit(false);
forecastSalesDOS.forEach(e -> {
try {
ps.setObject(1, e.getSku());
ps.setObject(2, e.getErpSku());
ps.setObject(3, e.getSite());
ps.setObject(4, e.getMarketId());
ps.setObject(5, DateUtil.getMinusHours(e.getPaidTime()));
ps.setObject(6, e.getAmount());
ps.setObject(7, e.getPlatformId());
ps.setObject(8, e.getOrderType());
ps.setObject(9, e.getOrderCategory());
ps.setObject(10, e.getAsin());
ps.setObject(11, e.getIdealWarehouse());
ps.setObject(12, e.getActualFreight());
ps.setObject(13, e.getActualWarehouse());
ps.setObject(14, e.getCountry());
ps.setObject(15, e.getSimulatedNetProfit());
ps.setObject(16, e.getOrderTotalPrice());
ps.setObject(17, e.getOrderCode());
ps.setObject(18, e.getOrderCurrency());
ps.setObject(19, DateUtil.getMinusHours(e.getOrderCreatedTime()));
ps.setObject(20, e.getOrderSn());
ps.setObject(21, e.getFreight());
ps.setObject(22, e.getUnitPrice());
ps.setObject(23, e.getIsFba());
ps.setObject(24, DateUtil.getMinusHours(e.getCreatedTime()));
ps.setObject(25, e.getCreatedBy());
ps.setObject(26, DateUtil.getMinusHours(e.getUpdatedTime()));
ps.setObject(27, e.getUpdatedBy());
ps.setObject(28, e.getIsDelete());
ps.addBatch();
// ps.clearParameters();
} catch (SQLException throwables) {
log.info("内层:JDBCOperationServiceImpl->batchForecastSales:异常,{}", throwables.getMessage());
throw new RuntimeException(throwables);
}
});
DbUtil.executeBatchInsert(connection, ps);
dbPool.releaseConnection(connection);
} catch (SQLException throwables) {
throwables.printStackTrace();
log.info("外层:JDBCOperationServiceImpl->batchForecastSales:异常,SQLException::{}", throwables.getMessage());
count++;
log.info("尝试中:::::");
this.saveForecastSales(forecastSalesDOS, count);
}
}
public static void executeBatchInsert(Connection connection, PreparedStatement ps) throws SQLException {
if(ps==null){
log.info("executeBatchInsert->为空");
}
ps.executeBatch();
connection.commit();
ps.clearBatch();
closePs(ps);
}
```
一个能打的都没有吗?
500万都在forecastSalesDOS这个属性里?如果是的话那这些问题都可能是一个问题:数据库连接超时,connect属性可能已经超时了,导致问题2空指针异常,问题一明显是超时问题了,建议:
1.如果批量一次是500万insert的话建议少量多次循环批量且加事务,避免超时
2.在executeBatch前再向之前connect判断一下是不是还可用或者null这种情况
产生的原因:应用方的数据库连接有效期时间,大于数据库自己设置的有效期。
解决方案:
一、修改druid配置(如果使用druid的话)
1
2
3
4
spring.datasource.druid.validationQuery=select 1
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=true
spring.datasource.druid.testOnReturn=true
PS.此方案对性能会有一定影响
二、修改数据库连接配置
在数据库连接上,加“&autoReconnect=true&failOverReadOnly=false”配置
三、修改数据库连接有效时间
在数据库配置上设置,把数据库连接有效时间设置长一点,比如设置12小时或者24小时
四、还可以参考这篇文章的其它方法:https://www.cnblogs.com/jpfss/p/7206912.html
第一个问题应该是数据库连接超时了,一般连接可能就1分钟左右,网上搜索设置数据库连接超时时间的解决方案,第二个是ps创建对象没成功,你直接手动创建一个,第三个是不是一次性提交的数据太多了,sql过长也会报错,建议分批次提交,每次100条,然后用多线程调用,速度也不会慢