我正在寻找一种在合理的时间内处理从数据库加载的大量数据的方法。
我面临的问题是我必须从数据库中读取所有数据(当前大约30M行),然后用Java处理它们。处理本身不是问题,但从数据库中获取数据却是问题。提取通常需要1-2分钟。但是,我需要它快得多。我正在使用以下查询将数据从db直接加载到DTO:
select id, id_post, id_comment, col_a, col_b from post_comment
在id
是主键的情况下,id_post
和id_comment
是各个表的外键,而col_a
和col_b
是小的int数据类型的列。具有外键的列具有索引。
我目前用于这项工作的工具是Java,Spring Boot,Hibernate和PostgreSQL。
到目前为止,我想到的唯一选择是
我错过了什么吗?或者这是我唯一的选择?我愿意接受任何想法。 请注意,我只需要读取数据,而无需进行任何更改。
编辑:使用的查询的解释分析
"Seq Scan on post_comment (cost=0.00..397818.16 rows=21809216 width=28) (actual time=0.044..6287.066 rows=21812469 loops=1), Planning Time: 0.124 ms, Execution Time: 8237.090 ms"
答案 0 :(得分:1)
您是否需要一次处理所有行,还是可以一次处理一个行?
如果一次可以处理它们,则应尝试使用可滚动的结果集。
org.hibernate.Query query = ...;
query.setReadOnly(true);
ScrollableResults sr = query.scroll(ScrollMode.FORWARD_ONLY);
while(sr.next())
{
MyClass myObject = (MyClass)sr.get()[0];
... process row for myObject ...
}
这将仍然记住实体管理器中的每个对象,因此会变得越来越慢。为避免该问题,您可以在完成后从实体管理器分离对象。仅在未修改对象的情况下才可以这样做。如果对它们进行了修改,则更改将不会保留。
org.hibernate.Query query = ...;
query.setReadOnly(true);
ScrollableResults sr = query.scroll(ScrollMode.FORWARD_ONLY);
while(sr.next())
{
MyClass myObject = (MyClass)sr.get()[0];
... process row for myObject ...
entityManager.detach(myObject);
}
答案 1 :(得分:1)
如果我不知所措,我肯定会绕过hibernate并直接转到JDBC进行此查询。 Hibernate不是用于处理大型结果集的,它代表了不适用于此类情况的额外开销。
使用JDBC时,请不要忘记将autocommit设置为false并设置较大的提取大小(成千上万个数量级),否则postgres将首先将所有2100万行提取到内存中,然后再开始将它们提供给您。 (请参见https://stackoverflow.com/a/10959288/773113)
答案 2 :(得分:1)
自从您提出想法以来,我已经看到此问题在以下选项中得以解决,具体取决于它在您的环境中的适合程度: 1)首先尝试使用JDBC和Java,简单的代码,然后可以对数据库和数据进行测试,以查看此改进是否足够。您将需要在这里牺牲Hibernate的其他好处。 2)在第1点中,使用具有多个连接的多线程将数据拉到一个队列中,然后可以使用该队列进行进一步处理或根据需要进行打印。您也可以考虑使用Kafka。 3)如果数据要继续保持增长,可以考虑将Spark作为最新技术,它可以全部存储在内存中,并且速度更快。
这些是其中的一些选项,如果这些想法在任何地方都对您有帮助,请喜欢。
答案 3 :(得分:-1)
为什么30M保留在内存中? 最好将其重写为纯sql并使用基于id的分页
您将收到5条最后评论的ID,然后发出
select id, id_post, id_comment, col_a, col_b from post_comment where id > 5 limit 20
如果需要更新整个表,则需要将任务放在cron中,但也要在其中进行部分处理 储存30M的内存非常昂贵-您需要处理零件 0-20 20-n n + 20