at.*_*at. 52 java mysql hibernate large-data-volumes scrollableresults
我只需要使用Hibernate读取MySQL数据库中表中的每一行,并根据它编写一个文件.但是有9000万行,它们非常大.所以看起来以下是合适的:
ScrollableResults results = session.createQuery("SELECT person FROM Person person")
.setReadOnly(true).setCacheable(false).scroll(ScrollMode.FORWARD_ONLY);
while (results.next())
storeInFile(results.get()[0]);
Run Code Online (Sandbox Code Playgroud)
问题是上面将尝试将所有9000万行加载到RAM中,然后再转到while循环...这将使OutOfMemoryError消除我的内存:Java堆空间异常:(.
所以我猜ScrollableResults不是我想要的?处理这个问题的正确方法是什么?我不介意这个while循环需要几天(好吧我不喜欢它).
我想处理这个问题的另一种方法是使用setFirstResult和setMaxResults迭代结果,只使用常规的Hibernate结果而不是ScrollableResults.这感觉就像它效率低下一样,当我在8900万行中调用setFirstResult时,它将开始花费一段可笑的时间......
更新:setFirstResult/setMaxResults不起作用,事实证明需要花费相当长的时间才能达到我所担心的偏移量.这里一定有解决方案!这不是一个很标准的程序吗?我愿意放弃Hibernate并使用JDBC或其他任何东西.
更新2:我提出的解决方案哪个工作正常,不是很好,基本上是以下形式:
select * from person where id > <offset> and <other_conditions> limit 1
Run Code Online (Sandbox Code Playgroud)
由于我有其他条件,即使是索引中的所有条件,它仍然没有我想要的那么快......所以仍然可以提供其他建议..
Mic*_*ael 29
使用setFirstResult和setMaxResults是我唯一知道的选项.
传统上,可滚动的结果集只会根据需要将行传输到客户端.不幸的是,MySQL Connector/J实际上伪造了它,它执行整个查询并将其传输到客户端,因此驱动程序实际上已将整个结果集加载到RAM中并将滴滴送到您(由您的内存不足问题证明) .你有正确的想法,这只是MySQL java驱动程序中的缺点.
我发现无法解决这个问题,所以使用常规的setFirst/max方法加载大块.很抱歉成为坏消息的使者.
只需确保使用无状态会话,这样就没有会话级缓存或脏跟踪等.
编辑:
除非你突破MySQL J/Connector,否则你的UPDATE 2是最好的.虽然没有理由你不能提高查询的限制.如果你有足够的RAM来保存索引,这应该是一个有点便宜的操作.我稍微修改它,并一次抓取一批,并使用该批次的最高ID来获取下一批.
注意:这仅在other_conditions使用相等(不允许范围条件)并将索引的最后一列作为id时才有效.
select *
from person
where id > <max_id_of_last_batch> and <other_conditions>
order by id asc
limit <batch_size>
Run Code Online (Sandbox Code Playgroud)
小智 20
你应该可以使用a ScrollableResults
,虽然它需要一些神奇的咒语才能使用MySQL.我在一篇博客文章(http://www.numerati.com/2012/06/26/reading-large-result-sets-with-hibernate-and-mysql/)中写了我的发现,但我将在这里总结一下:
"[JDBC]文档说:
To enable this functionality, create a Statement instance in the following manner:
stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);
Run Code Online (Sandbox Code Playgroud)
这可以在Hibernate API的3.2+版本中使用Query接口(这也适用于Criteria)来完成:
Query query = session.createQuery(query);
query.setReadOnly(true);
// MIN_VALUE gives hint to JDBC driver to stream results
query.setFetchSize(Integer.MIN_VALUE);
ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);
// iterate over results
while (results.next()) {
Object row = results.get();
// process row then release reference
// you may need to evict() as well
}
results.close();
Run Code Online (Sandbox Code Playgroud)
这允许您对结果集进行流式传输,但是Hibernate仍会将结果缓存在中Session
,因此您需要经常调用session.evict()
或者session.clear()
每隔一段时间调用一次.如果你只是在阅读数据,你可以考虑使用a StatelessSession
,尽管你应该事先阅读它的文档."
Har*_*ris 17
将查询中的提取大小设置为最佳值,如下所示.
此外,当不需要缓存时,最好使用StatelessSession.
ScrollableResults results = session.createQuery("SELECT person FROM Person person")
.setReadOnly(true)
.setFetchSize( 1000 ) // <<--- !!!!
.setCacheable(false).scroll(ScrollMode.FORWARD_ONLY)
Run Code Online (Sandbox Code Playgroud)
小智 7
必须是FetchSize Integer.MIN_VALUE
,否则它将无效.
它必须从官方参考文字中获取:https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-implementation-notes.html
归档时间: |
|
查看次数: |
78053 次 |
最近记录: |