从数据库读取数千条记录时,我遇到了一些与性能有关的问题。我注意到纯JDBC查询比JPA本机查询要快得多。
这是查询
select ID, COL_A, COL_B, COL_C, COL_D, COL_E, COL_F from MY_SUPER_VIEW_V v
where 1=1
and v.ID in (:idList)
and v.DATE_FROM <= :date
and v.DATE_TILL >= :date;
此查询返回大约38.000条记录。
idList中的记录超过1000条,由于我使用的是Oracle DB,因此需要将其拆分为n个查询。
此外,我还有一种方法可以将Object []结果转换为我的List<Entity>
。
为了理解性能问题,我分别创建了一个纯JDBC查询和一个JPA Native查询来比较结果。
这是时间。
################ getScoresPureJDBCWithListIds ################
List of Ids retrieved. It took: 00:00:00.096 to execute query on DB using JDBC
It took: 00:00:01.180 to execute query on DB using JDBC query
Creating 24206 Scores records from DB result It took: 00:00:04.440
It took: 00:00:01.038 to execute query on DB using JDBC query
Creating 14445 Scores records from DB result It took: 00:00:04.307
################ getScoresJPANativeQueryWithListIds ################
It took: 00:06:09.450 to execute query on DB using JPA Native query
Creating 24206 Scores records from DB result It took: 00:00:00.009
It took: 00:04:04.879 to execute query on DB using JPA Native query
Creating 14445 Scores records from DB result It took: 00:00:00.007
使用Hibernate分析
################ USING FETCH_SIZE: 2000 ################
################ getSmartESGScoresPureJDBCWithListCsfLcIds ################
List of Securities CsfLcId retrieved. It took: 00:00:00.296 to execute query on DB using JDBC
It took: 00:00:11.940 to execute query on DB using JDBC query
Creating 24206 Smart Esg Scores records from DB result It took: 00:00:02.670
It took: 00:00:13.570 to execute query on DB using JDBC query
Creating 14445 Smart Esg Scores records from DB result It took: 00:00:02.553
################ getSmartESGScoresJDBCTemplateWithListCsfLcIds ################
List of Securities CsfLcId retrieved. It took: 00:00:00.087 to execute query on DB using JDBC
Creating 24206 Smart Esg Scores records from DB result It took: 00:00:04.063
Creating 14445 Smart Esg Scores records from DB result It took: 00:00:04.064
################ getSmartESGScoresJPANativeQueryAsESGenius with hint fetch size 2000 ################
2020-04-22 09:36:30.830 INFO 13262 --- [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
1232369 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
1448702 nanoseconds spent preparing 1 JDBC statements;
3992364 nanoseconds spent executing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
List of Securities CsfLcId retrieved. It took: 00:00:00.261 to execute query on DB using JDBC
2020-04-22 09:47:23.739 INFO 13262 --- [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
73670 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
805772 nanoseconds spent preparing 1 JDBC statements;
651947762290 nanoseconds spent executing 1 JDBC statements; ==> 10 minutes
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
It took: 00:10:52.898 to execute query on DB using JPA Native query
Creating 24206 Smart Esg Scores records from DB result It took: 00:00:00.018
2020-04-22 09:56:00.792 INFO 13262 --- [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
2758010 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
3096653 nanoseconds spent preparing 1 JDBC statements;
516148003151 nanoseconds spent executing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
It took: 00:08:37.032 to execute query on DB using JPA Native query
Creating 14445 Smart Esg Scores records from DB result It took: 00:00:00.006
对于JDBC查询,我可以看到1)执行查询的速度非常快,但是2)循环处理每个ResultSet元素花费的时间最多为00:09秒int total
另一方面,对于JPA本机查询,1)通过调用query.getResultList()方法执行查询,另一方面,它花费大量时间10:14秒2)在这里处理每个结果都非常快。 Analytics(分析)显示,执行1条JDBC语句花费了大量时间。即使FETCH_SIZE = 2000,也没有明显变化。
为什么与纯JDBC相比,JPA Native的速度很慢?会是类型转换吗?就我而言,我是在谈论varchar2和数字。我期望得到与JDBC相同的结果。但是从8秒到10分钟就很多了。
我该怎么做才能改善JPA本机查询?
答案 0 :(得分:3)
您似乎在比较两个不同的查询,很可能导致数据库使用不同的查询计划。
有很多方法可以调查问题,但是由于您没有提供可复制的最小示例,因此我们无法使用这些方法。因此,我建议您自己进行一些调查:
答案 1 :(得分:1)
请注意,如果要比较两个概念,则必须尝试隔离主要功能并摆脱其他因素,这可能会干扰结果。
因此,要查看如果JDBC查询和JPA本机查询的行为不同,我将提出以下方案:
仅对包含1000个元素的列表使用一个查询
使用普通表代替视图
这里有一个简单的设置可以验证性能。该表每个GRP_ID
都有50行,因此每1000个键可获得5万行(请参阅下面的脚本来设置表)
List params = (13001L..14000L)
def query = session.createNativeQuery("select * from tab where grp_id in (:paramsList) ")
query.setFetchSize(2000)
query.setParameterList("paramsList", params);
result = query.getResultList();
样品运行显示此结果
got 50000 rows in 1.388 seconds
所以我觉得没有必要使用普通的JDBC重复测试,您会看到可比的结果。
更有趣的是重复运行并删除该行
query.setFetchSize(2000)
将有效地将获取大小重置为默认值(在我的情况下为20),相同数据的结果为
got 50000 rows in 1 minutes, 0.903 seconds
1)因此,访存大小是观察到的行为的最可能的解释。重要的是要检查JDBC驱动器是否具有正确的值并使用它-毫无疑问,您必须使用10046跟踪来查看使用数据库的读取大小。但是对我来说,以上说法非常有效。
2)本地JPA查询与手动编写的JDBC执行+获取已准备好的语句之间没有实质性差异,这可以解释您的观察。两者都在数据库中执行语句执行 ,然后执行许多 fetches -计数取决于使用的 fetch大小
3)当然,视图也可以产生影响,但是在 query 中会有所不同-不在 JDBC v之间。 JPA 。
4),您没有提及它,因此在此不做详细说明,并假定您的视图不包含任何CLOB
列。这当然可以发挥作用。
5)最后一点是您提到的两个查询-您使用两个独立查询还是一个OR
串联IN列表查询?您不会提供详细信息,因此很难发表评论。无论如何,两个独立的查询应该没有影响。
说了警告一句话。
IN列表计数的限制有其用途。对于临时脚本来说,使用大的IN列表选择是可以接受的,但是对于常规运行的查询,这可能是 解析问题。为什么?
您可以使用绑定变量将以下quereis视为单个语句(仅解析一次)
select * from tab where ID = 1
select * from tab where ID = 2
导致
select * from tab where ID = ?
但是以下两个查询(IN列表的长度不同)仍然不同,必须分别进行解析
select * from tab where ID in ( ? )
select * from tab where ID in ( ?, ? )
因此,如果出于您的目的使用3万行以上的设备,那么休眠是最好的选择
Hibernate旨在优雅地了解使用SQL 的需求,这被大多数开发人员认为是很酷的想法(与大多数DB人相反具有相反的意思;)。
这个概念很好用,用例越简单越好。另一方面,对于批处理,有时最好直接使用SQL 进行处理
测试数据
create table tab as
select
rownum id,
trunc(rownum / 50) +1 grp_id,
rpad('x',100,'y') pad
from dual connect by level <= 1000000;
create index idx on tab(grp_id);
答案 2 :(得分:0)
JDBC通常比JPA快,但是在JPA中,您可以从缓存中受益,并且这种方式可以获得更好的性能。
我不知道此查询的目的以及如何使用(报告?),但是您应该考虑使用其他条件,而仅列出许多id。我怀疑有些用户手动选择了1000多个ID,因此我想他们是根据其他一些标准选择的。尝试改用这种肌酸。