我知道与此相似的问题很多次,但是即使尝试了许多解决方案,我仍然看到了这个问题。
我们的应用程序允许技术用户创建参数化的RAW SQL查询,以从下载到Excel电子表格的DB中提取数据。
对于较小的数据集,这可以正常工作,但是,当文件大小开始接近10MB 时,我开始遇到此问题。
数据集可能是100k行或80-90MB的大小。如果可能的话,我不想增加JVM堆的大小。
希望我的代码中有一个明显的错误,但我尚未发现。ResultSet.next()循环似乎是问题的根源。是否有更有效的方法来停止吞噬堆空间?
任何帮助。谢谢
/*
*
* query is a raw sql query that takes parameters (using Mybatis)
* criteriaMap the arguments that we subsitute into the query
*
*/
public List<Map<String, Object>> queryForJsonWithoutMapping(final String query, final Map<String, Object> criteriaMap){
SqlSession sqlSession = getSqlSessionInstance();
String sql = "";
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet resultSet = null;
try {
final Configuration configuration = getSqlSessionInstance().getConfiguration();
SqlSourceBuilder builder = new SqlSourceBuilder(configuration);
SqlSource src = builder.parse(query, Map.class, null);
BoundSql boundSql = src.getBoundSql(criteriaMap);
sql = boundSql.getSql();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
connection = sqlSession.getConnection();
pstmt = connection.prepareStatement(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY);
// this function subs the params into the preparedStatement query
buildParams(parameterMappings, criteriaMap, pstmt);
resultSet = pstmt.executeQuery();
// the while loop inside this function is where things start to hang
List<Map<String, Object>> results = getObjectFromResultSet(resultSet);
return results;
} catch (Exception e) {
LOG.error(e.getMessage(), e);
LOG.error(ExceptionUtils.getStackTrace(e));
throw new IllegalStateException(sql + " " + e.getMessage(), e);
} finally {
try{
connection.close();
pstmt.close();
resultSet.close();
}catch (SQLException e){
e.printStackTrace();
}
sqlSession.close();
}
private List<Map<String, ?>> getEntitiesFromResultSet(ResultSet resultSet) throws SQLException {
ArrayList<Map<String, ?>> entities = new ArrayList<>(resultSet.getFetchSize());
int index = 0;
Map<String, Object> jsonObject;
while (resultSet.next()) {
jsonObject = getEntityFromResultSet(resultSet);
entities.add(index, jsonObject);
index ++;
}
resultSet.close();
return entities;
}
private List<Map<String, Object>> getObjectFromResultSet(ResultSet resultSet) throws SQLException {
ArrayList<Map<String, Object>> entities = new ArrayList<>(resultSet.getFetchSize());
int index = 0;
Map<String, Object> jsonObject;
while (resultSet.next()) {
jsonObject = getEntityFromResultSet(resultSet);
entities.add(index, jsonObject);
index ++;
}
resultSet.close();
return entities;
}
db是oracle
从一个go中获取和处理所有行中的所有行是一个坏主意。您需要实现分页的通用想法即可一次阅读和处理一个页面( n = page-size 行)。
您的页面大小应该足够最佳,以至于您不会发出太多的DB命中,同时又不会在内存中拥有太多记录。
Spring Batch API的JDBCPIGENEMRERERRERERRERERERENER实现了此概念。
请参考这个问题,以获取有关JDBC分页的更多想法。
除此之外,您不应该继续增加地图results
的大小。您需要用周期冲洗这张地图。
希望这会有所帮助!
在这样的设计中,如果查询的结果返回大量数据,则在某个时候不可避免地会用完存储器,因为您将整个结果集加载到内存中。取而代之的是,您可以简单地说明您的数据量在数据量方面具有阈值。对于每行,您都可以计算其大小,并决定是否可以将其添加到JSON DOC中。如果您经过了阈值,则您停止并关闭结果集(这将取消服务器上的执行)。另一个选项将涉及流式传输结果,但这更复杂。