【问题标题】:Hibernate generated query is slow in application but fast when executed manuallyHibernate 生成的查询在应用程序中很慢,但在手动执行时很快
【发布时间】:2018-01-02 12:50:08
【问题描述】:

我将 Spring DATA JPA 与 Hibernate + PostgreSQL 一起使用,并生成如下所示的 q 查询:

SELECT A.field1 AS field1_10_,
       A.field2 AS field2_10_,
       A.field3 AS field3_10_,
       ...
       A.field10 as field10_10_
FROM mytable A
WHERE (A.field1 BETWEEN 1 AND 2)
  AND ((cast(A.field1 AS varchar(255))||cast(A.field2 AS varchar(255))) IN
         (SELECT (cast(B.field1 AS varchar(255))||cast(max(B.field2) AS varchar(255)))
          FROM mytable B
          WHERE B.field1 BETWEEN 1 AND 2
          GROUP BY B.field2))
LIMIT 20

所有AB 字段都是数字。

如果我复制该查询并直接在 Postgres 中执行它(使用 Pgadmin),不到一秒就会得到结果。但在我的应用程序中,Hibernate 检索结果大约需要一分钟。我已经激活了 Hibernate 统计信息,并且总时间是运行 JDBC 语句。

我正在使用 Spring 分页,它使用相同的规范生成计数查询。此查询运行速度很快(不到一秒):

SELECT count(*) AS col_0_0_
FROM mytable A
WHERE (A.field1 BETWEEN 1 AND 2)
  AND ((cast(A.field1 AS varchar(255))||cast(A.field2 AS varchar(255))) IN
         (SELECT (cast(B.field1 AS varchar(255))||cast(max(B.field2) AS varchar(255)))
          FROM mytable B
          WHERE B.field1 BETWEEN 1 AND 2
          GROUP BY B.field2))

有什么想法吗?

更新:我刚刚发现PostgreSQL执行的生成语句如下:

SELECT A.field1 AS field1_10_,
       A.field2 AS field2_10_,
       A.field3 AS field3_10_,
       ...
       A.field10 as field10_10_
FROM mytable A
WHERE (A.field1 BETWEEN ? AND ?)
  AND ((cast(A.field1 AS varchar(255))||cast(A.field2 AS varchar(255))) IN
         (SELECT (cast(B.field1 AS varchar(255))||cast(max(B.field2) AS varchar(255)))
          FROM mytable B
          WHERE B.field1 BETWEEN ? AND ?
          GROUP BY B.field2))
LIMIT $1

请注意,BETWEEN 条件中的替换字符是问号?,但限制是美元以及参数订单$1。限制是由 Spring 分页自动添加的,如果我删除它,查询会很快。

更新 2:我刚刚发现问题仅在指定限制为 1 时才会发生,更大的值会按预期工作。很奇怪。

【问题讨论】:

  • 如何调用查询?你把它当作原生的吗?
  • 没有。我使用 Criteria Query 生成规范并将其传递给 JPA 存储库方法
  • 您的 Spring 应用程序中的事务隔离级别是什么?如果驱动程序等待数据库,您能否添加有关它在那一分钟内所做的事情的 JDBC 驱动程序调试日志以及可能的 Postgres 日志?

标签: spring hibernate spring-data


【解决方案1】:

有多种可能的原因。

  1. JPA 将在返回结果之前获取所有行。确保 PGAdmin 在停止计时器之前滚动到结果的最末尾也这样做。

  2. JPA(在某些情况下是 Spring)会将您从数据库中获取的 ResultSet 转换为实体或其他一些 Java 对象。如果你在构造函数或 getter 中做一些重要的事情,这可能会导致性能下降。此外,如果您的会话非常大,这也可能会导致性能问题。一旦你附加了一个分析器,这些应该会变得很明显。

  3. 查询必须从规范创建(在您的情况下),这也可能需要一些时间。同样:附加一个分析器来识别这一点。

【讨论】:

  • (1) 我将结果限制为 20 行,所以我什至没有滚动条。 (2) 我做的很少见。此外,我使用预测来填充结果。 (3) 我正在使用 Spring 分页功能,它根据相同的规范生成计数查询。该查询运行速度很快(不到一秒),所以问题一定出在 Select 语句中(我会在一分钟内发布 count 查询)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-06
  • 2011-06-27
  • 2022-11-18
  • 2016-06-28
  • 2020-12-21
  • 2021-12-13
  • 1970-01-01
相关资源
最近更新 更多