【发布时间】:2015-01-09 09:51:22
【问题描述】:
首先我想说,我远不是 JPA 和 Hibernate 方面的专家。但是我遇到了一些我完全陷入困境的事情,因为我不了解事情在下面是如何工作的,也不知道如何调试它。
我有一个网络应用程序(Spring + JPA2.0)。我有一个算法,可以使用来自 PostrgeSQL 数据库的数据为用户计算分数。为了衡量算法的速度,我对 1 个用户进行了计算。
耗时 72 秒。 (我觉得这次有点奇怪,因为我知道它不应该花那么长时间)
所以我所做的就是用简单的 JDBC 连接 (conn = DriverManager.getConnection(url, props);)) 替换数据库调用 (entityManger.createQuery(...)),然后我在不使用 JPA 的情况下从数据库中提取数据。
计算同一用户的分数需要 35 秒...
使用 JPA 会使计算变慢 50% 是否正常?我的意思是我会认为是的,时间会比简单地使用 jdbc 多“一点”,而是 50% ??
是否有任何我不知道的配置可以解释这种差异?
这是算法的伪代码。 calculateScore 在循环内调用 2000 次:
public double calculateScore(userId, placeId) {
//This returns in average 5 columns
List<Object[]> datas = entityManager.createNativeQuery("SELECT info1, info2, info3 WHERE user_id=.. and place_id=...").getResultList();
..
..
for (Object[] result : datas) {
info1 = result[0] // Integer
info2 = result[1] // Integer
info3 = result[2] // Double
if (info3 == null) {
info3 = calculateMissing(..)
}
.. calculation ..
return result
}
private double calculateMissing(..) {
//this query returns maximum 20 rows
List<Double> data = entityManager.createNativeQuery("SELECT info FROM..."). getResultList;
//So here we could have UP TO 20 call to the db
while(some cond) {
data2 = entityManager.createNativeQuery("SELECT * FROM...).getOne()
if (data2 ..)
cond = false
..
}
... calculation ..
return result;
}
所以这是我对数据库的“三个”小选择。
编辑 1
我已经尝试缩小问题的“确切”位置。我已经一一替换了从 jdbc 到 JPA 的调用。似乎while循环是造成50%(几乎)时间损失的循环。从一开始就做 dis 是一个非常糟糕的主意,但我不知道 JPA 根本不会喜欢这个。
我已将 while 循环替换为更智能的 SQL 查询。现在算法在 15 秒而不是 70 秒内运行!
在 12 秒内,而不是 JDB 版本的 35 秒。
【问题讨论】:
-
JPA 肯定会引入开销,尤其是与反射相关的开销,但这种情况表明您的方法存在根本性的问题。
-
我将编辑我的问题以包含有关选择类型、数据大小等的更多详细信息。
-
我猜,您的查询检索到的数据远少于使用 JPA 时检索到的数据(可能是由于急切地获取某些集合等)。
-
代码异味:从数据库中获取结果列表后,您正在对结果列表执行一些极端操作,而无需过滤(如果有),这些操作应该在数据库端(通过 JPA)执行。在循环内部执行一条 SQL 语句,先得到一个泛化列表,然后在前端对列表执行操作,这似乎不是一个好的做法。本质上,这些测试用例似乎不是健全的测试用例(虽然 ORM 基本上往往有点慢,但如果精确使用优化技术,那应该不会太多)。
-
至少部分问题在于您不断地重新创建而不是重复使用查询,但这看起来像是您应该在 SQL 中执行的那种聚合操作并只返回结果。
标签: spring performance jpa slowdown