【发布时间】:2020-08-03 06:09:39
【问题描述】:
我在从数据库读取数千条记录时遇到了一些与性能相关的问题。我注意到纯 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 秒 int 总
另一方面,对于 JPA Native 查询 1) 通过调用 query.getResultList() 方法执行查询需要大量时间 10:14 秒另一方面 2) 在这里处理每个结果非常快。分析表明,执行 1 条 JDBC 语句需要花费大量时间。即使 FETCH_SIZE = 2000 也没有显着变化。
与纯 JDBC 相比,为什么 JPA Native 很慢? 会不会是类型转换?就我而言,我说的是 varchar2 和数字。我期待与 JDBC 相同的结果。但从 8 秒到 10 分钟的时间很多。
我可以做些什么来改进 JPA Native 查询?
【问题讨论】:
-
v.ID in (:idList)这很可疑。你在 JDBC 调用中的:idList中传递了什么?逗号分隔的值列表? -
同样首先要验证的是,如果两个方法都使用相同(且适当)的 fetch size 值。
-
在 JDBC 案例中,我通常设置参数 scorePreparedStatement.setLong(i+3, partition.get(i)) 但在查询中,我必须在 (?, ?, ?, ?, ........, ?)
-
重读您的问题后,无论是对于 JPA 还是对于 JDBC,这都不可行。 IN 列表旨在解决 少数 个不是 24K 的 ID。 sql_trace JPA 语句,您将看到连接 IN 列表谓词的大量 OR,这显然会使优化器感到困惑。正确的方法是找到一个没有长 IN 列表的解决方案。
-
@ Marmite Bomber inList “just” 有 1600 个元素(这意味着 Oracle 中有 2 个查询,1 个查询 1000,另一个查询 600)。您看到的 24k 是结果中的记录数。