在java中获取数百万条记录

时间:2014-04-10 20:24:08

标签: java hibernate jdbc

非常开放的问题, 我需要编写一个java客户端,它从Oracle数据库中读取数百万条记录(比如说帐户信息)。将其转储为XML并通过webservices将其发送给供应商。

最优化的方法是什么?从获取数百万条记录开始。我走了JPA / hibernate路线,我从记忆错误中获取了200万条记录。

JDBC是更好的方法吗?在我去的时候获取每一行并构建XML?还有其他选择吗?

我不是Java方面的专家,所以我们非常感谢任何指导。

7 个答案:

答案 0 :(得分:2)

我们在某个时候遇到了类似的问题,我们的记录大小超过了2M。这就是我们接近的方式。

  • 由于大量开销(如创建大型POJO),如果要将数据转储到XML,基本上不需要使用任何OR映射工具。

  • 简单的JDBC是要走的路。这样做的主要优点是它返回一个ResultSet对象,实际上并不包含所有结果。因此解决了在内存中加载整个数据的问题。在我们遍历ResultSet

  • 时加载数据
  • 接下来是XML文件的创建。我们创建一个XML文件并打开,而不是Append mode

  • 现在循环遍历Resultset对象,我们创建XML片段,然后将其附加到XML文件。这一直持续到整个Resultset被迭代。

  • 最后我们所拥有的是XML文件将所有记录。

  • 现在,为了共享此文件,我们创建了一个Web服务,如果文件可用,它将返回此XML文件的URL(存档/压缩)。

  • 客户端可以在此后的任何时间下载此文件。

  • 请注意,这不是同步系统,这意味着客户端进行呼叫后该文件不可用。由于创建XML调用需要花费大量时间,因此HTTP wold通常会超时,因此这种方法。

您可以从中获取线索。希望这会有所帮助。

答案 1 :(得分:1)

对于这个大小的数据,你可能可以通过启动更多内存的java来逃脱。启动Java时,请使用-Xmx-Xms签出。

如果您的数据真的太大而无法存储在内存中,但又不足以保证对不同技术的投资,请考虑以块的形式运行。这是否必须立即完成?你可以将数据分成10个块并独立完成每个块吗?如果必须一次完成,你可以从数据库中流式传输数据,然后将其流式传输到文件中,忘记你所做的事情(保持JVM中的内存使用率低)?

答案 2 :(得分:1)

使用ResultSet#setFetchSize()优化从数据库中获取的记录。

请参阅What does Statement.setFetchSize(nSize) method really do in SQL Server JDBC driver?

  

在JDBC中,ResultSet#setFetchSize(int)方法非常重要   JVM控制时的性能和内存管理   从JVM到数据库的网络调用次数   相应地用于ResultSet处理的RAM量。

请在此处阅读Oracle ResultSet Fetch Size

答案 3 :(得分:1)

  1. 按照之前的答案解释,以块的形式阅读记录。

  2. 使用StAX http://stax.codehaus.org/将记录块流式传输到XML文件,而不是将所有记录流式传输到一个大型文档中

  3. 保持冷淡

答案 4 :(得分:0)

就Hibernate方面而言,使用SELECT查询(而不是FROM查询)来获取以防止填充缓存;或者使用statelessSession。另外,请务必使用scroll()代替list()。建议将hibernate.jdbc.fetch_size配置为类似200的内容。

在响应方面,XML是一个非常糟糕的选择,因为解析很困难。如果已设置,请确保使用流式XML序列化程序。例如,XPP3库包含一个。

答案 5 :(得分:0)

虽然合理的Java方法可能涉及将XML的StAX构造与分页结果集(简单的JDBC或JPA)结合使用,但请记住,您可能需要一直锁定数据库以进行更新,这可能会或可能会在你的情况下是不可接受的。

我们采用了一种不同的,以数据库为中心的方法,使用INSERTUPDATE上的存储过程触发器来生成对应的XML节点每行/ [块]数据。这样可以不断确保250GB +的原始数据及其XML表示(~10 GB)是最新的,并且可以减少(没有双关语)导出到单纯的连接问题。

答案 6 :(得分:0)

你仍然可以使用Hibernate来获取数百万的数据,只是你不能在一轮中完成它,因为数百万是一个很大的数字,当然你会有内存不足的例外。您可以将其划分为页面,然后每次都转储到XML,这样记录就不会保存在RAM中,您的程序也不需要如此大的内存。

我之前的项目中有两种常用的方法。不幸的是我不喜欢使用HQL,所以我没有代码。

所以这里INT_PAGE_SIZE是你想要每轮获取的行数,而getPageCount是获取要获取所有记录的总轮数。 然后paging将逐页获取记录,从1到getPageCount

public int getPageCount(Criteria criteria) {
    ProjectionList pl = Projections.projectionList();
    pl.add(Projections.rowCount());
    criteria.setProjection(pl);
    int rowCount = (Integer) criteria.list().get(0);
    criteria.setProjection(null);
    if (rowCount % INT_PAGE_SIZE == 0) {
        return rowCount / INT_PAGE_SIZE;
    }
    return rowCount / INT_PAGE_SIZE + 1;
}

public Criteria paging(Criteria criteria, int page) {
    if (page != -1) {
        criteria.setFirstResult((page - 1) * INT_PAGE_SIZE);
        criteria.setMaxResults(INT_PAGE_SIZE);
    }
    return criteria;
}