使用JdbcTemplate获取和发送数百万条记录的最佳实践

时间:2016-03-14 07:54:42

标签: java database jdbc apache-camel spring-jdbc

在我的Web应用程序中,用户可以编写自己的sql查询并可以创建数据集。我使用Apache Camel的REST组件和Spring JDBC与JdbcTemplate来执行查询。但是在执行查询时,返回大量记录(大约数百万),抛出以下堆栈跟踪引发异常:

val labeled = pca.transform(trainingDf).rdd.map(row => LabeledPoint(
   row.getAs[Double]("label"),   
   row.getAs[org.apache.spark.mllib.linalg.Vector]("pcaFeatures")
))

以下是方式,我用来查询数据库并发送响应:

REST端点

org.apache.camel.CamelExecutionException: Exception occurred during execution on the exchange:...
.....
.....
Caused by: java.lang.OutOfMemoryError: Java heap space

执行查询的方法:

rest("/abc").consumes("application/json").produces("application/json")
.get("/xyz").to("bean:d?method=getXYZ(${body})").outTypeList(Map.class)

我尝试使用public List<Map<String,Object>> getXYZ(Map<String,Object> details) { JdbcTemplate jdbc=new JdbcTemplate(ds); //ds is DataSource Object List<Map<String,Object>> resultSet=jdbc.query(details.get("query").toString(), new RowMapper<Map<String,Object>>() { @Override public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException { Map<String,Object> map = new HashMap<String,Object>(); DateFormat nice = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); ResultSetMetaData meta = rs.getMetaData(); for(int i = 1; i <= meta.getColumnCount(); ++i) { Object o = rs.getObject(i); if(o instanceof Date) { o = nice.format(o); } //put into map. map.put(meta.getColumnLabel(i), o); } return map; } }); return resultSet; } 设置获取大小,并尝试使用jdbc.setFetchSize(1000)增加堆内存,但无法对其进行排序。

使用如此大的数据查询,获取和发送响应的最佳做法是什么?

什么是创建java.lang.OutOfMemoryError?

有没有办法在客户端压缩数据和自动解压缩?或流式传输结果集?

1 个答案:

答案 0 :(得分:0)

OutOfMemory正在发生,因为大量对象被加载到您的应用程序中。

让我们假设只加载了String对象。 Java为每个字符存储2个字节,因此可以想象所有正在加载的字符串长度为5个字符。

让我们创建非常近似的计算。 5个字符的字符串对象是10个字节。其中1000个大约10千字节,1000000大约10兆字节。

我认为你的对象不仅仅是5个字符的字符串,所以你可以想象你的内存可以用得多快。

我强烈建议您将用户限制为有限的行数并引入分页。因此,即使用户没有在他的SQL中输入限制和偏移量,我也会以编程方式添加,以避免出现此问题。