我在从数据库中阅读数千条记录时遇到了一些与性能相关的问题。我注意到纯JDBC查询比JPA Native查询快得多。
下面是查询
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 ################
################ getScoresPureJDBCWithListIds ################
List of <elements> 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 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 records from DB result It took: 00:00:02.553
################ getScoresJDBCTemplateWithListIds ################
List of <elements> retrieved. It took: 00:00:00.087 to execute query on DB using JDBC
Creating 24206 records from DB result It took: 00:00:04.063
Creating 14445 records from DB result It took: 00:00:04.064
################ getScoresJPANativeQueryAsApplication 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 <ids> 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 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 records from DB result It took: 00:00:00.006
对于JDBC查询,我可以看到1)执行查询相当快,但2)处理循环中的每个ResultSet元素花费了大部分时间00:09 seconds int total
另一方面,对于JPA Native查询1)通过调用query.getResultList()方法执行查询需要花费大量时间10:14秒2)处理每个结果在这里相当快。分析表明,执行一条JDBC语句花费了大量的时间。即使使用FETCH_SIZE = 2000,也没有显著变化。
**为什么JPA Native与纯JDBC相比非常慢?**是否是类型转换?在我的例子中,我谈论的是varchar2和数字。我希望得到与JDBC相同的结果。但从8秒到10分钟,这是很多。
我可以做些什么来改进JPA Native查询?
3条答案
按热度按时间huwehgph1#
您似乎在比较两个不同的查询,很可能导致数据库产生不同的查询计划。
有很多方法可以研究这个问题,但没有一种方法对我们可用,因为你没有提供一个最小的可重复的例子。因此,我会建议你自己调查这个问题的一些选择:
mm5n2pyu2#
请注意,如果你想比较两个概念,你必须尝试隔离主要特征,并摆脱其他因素,这可能会干扰结果。
因此,为了看看JDBC查询和JPA原生查询在行为上是否有差异,我提出了以下场景:
这里有一个简单的设置来验证性能。表中每个
GRP_ID
有50行,因此1000个键有50 K行(请参阅下面的脚本来设置表)示例运行显示了此结果
所以我认为没有必要用普通JDBC重复测试,你会看到一个类似的结果。
更有趣的是重复运行并删除线
这将有效地将获取大小重置为默认值(在我的情况下为20),相同数据的结果是
**1)**因此,获取大小是观察到的行为的最可能的解释。重要的是检查JDBC驱动器是否获得了正确的值并使用了它-如果有疑问,您必须使用10046跟踪来查看数据库使用了多大的获取大小。但对我来说,上述声明工作得很好。
**2)**原生JPA查询和手动编写的JDBC execute + fetch预准备语句之间没有实质性差异,这可以解释您的观察结果。两者都执行 * 在数据库中执行语句 *,然后执行多次 * 获取 * -计数取决于使用的 * 获取大小 *
**3)**当然,view 也会有影响,但这将是 query 中的差异-而不是 JDBC与 * jdbc之间的差异。JPA。
**4)**您没有提到它,所以我在这里不详细介绍,并假设您的视图不包含任何
CLOB
列。这当然可以发挥作用。**5)**最后一点是在你提到的 * 两个查询 * -你使用两个独立的查询或一个查询whith
OR
串联在一起的IN列表?你没有提供细节,所以很难评论。无论如何,两个独立的查询应该没有影响。说了一句警告的话。
IN列表计数的限制有其目的。对于 * ad Hoc脚本 * 来说,使用大的IN列表选择是可以接受的,但是对于常规运行的查询,这可能是一个 * 解析问题 *。为什么?为什么?
使用绑定变量可以将以下查询视为单个语句(仅解析一次)
这导致
但是下面的两个查询(具有不同长度的IN列表)* 仍然是不同的,必须分别进行额外的解析 *
因此,重新考虑一下,对于您的目的,30 K行+Hibernate是否是最好的选择。
Hibernate的设计是为了优雅地满足 * 使用SQL* 的需要,这被大多数开发人员认为是一个 * 很酷的想法 *(与大多数DB人相反,他们有相反的意思;).
这个概念工作得很好,用例越简单越好。另一方面,对于 * 批处理 *,有时直接使用SQL更好。
测试数据:
abithluo3#
JDBC通常比JPA快,但在JPA中,您可以从缓存中受益,从而获得更好的性能。
我不知道这个查询的目的和如何使用(报告?),但你应该考虑使用不同的标准,然后只是列出这么多的id。我怀疑有些用户已经选择了1000+身份证手动,所以我猜他们是选择了一些其他标准批。尝试使用此创意。