【问题标题】:JPA/Hibernate performanceJPA/休眠性能
【发布时间】:2015-05-26 14:59:16
【问题描述】:

我的 Java EE 应用程序适用于两个表应用程序的相当大的关系数据库。每行 350 万行。这两个表被命名为“用户”和“设备”——在“设备”表上使用 FK 一对多。 我需要为用户提取设备。查询很简单:

SELECT * FROM Device d WHERE d.user_id = 'some_id'

我使用的是 SQL Server 2012,这个查询需要不到 1 毫秒的时间来运行。但是当我使用 Criteria API 或 JPQL 或 Native JPA Query 时,它需要超过 1 秒!

如果我将 Collection 存储在用户的类中并懒惰地获取设备,则需要超过 200 毫秒。

我正在使用休眠。

也许缺少信息来回答我的问题,如果是这样,请说明我应该提供哪些信息。

那么,当使用 JPA 处理大型数据集(如我所提到的 350 万)时,性能如此低下的原因可能是什么?

抱歉英语不好。

更新。 代码,需要 200 毫秒

Collection<Device> userDevices = user.getDevices();
for (Device device : userDevices) {
    if (device.getActive() && device.isToken())
        sender.sendSms(user.getMobilePhone(), text); // costs 0, i am using a stub
}

还有用户类:

@Entity
public class User {
    @Id
    private String userId;
    private String name;
    //bla bla bla
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user")
    private Collection<Device> devices;
    //bla bla bla

    public Collection<Device> getDevices() {
        return devices;
    }
}

【问题讨论】:

  • index你的数据了吗?
  • 索引与否,JPA 处理 Java 对象,并且想象将 ResultSet 中的数百、数千甚至数百万行转换为 Java 对象集合不会有任何开销,这有点天真。如果您有具体问题,您需要发布一些代码和映射以及一些时间安排。
  • 您的用户“some_id”有多少台设备?我们说的是 O(10) 还是 O(10000)? Hibernate 在您的测试用例中执行了哪些 SQL 查询?您确定您没有测量在代码中打开连接所用的时间吗?还是用于创建会话工厂的时间?发布您的测试用例的代码。
  • 请尝试记录 Hibernate 的 SQL 查询。看看docs.jboss.org/hibernate/orm/4.3/topical/html/logging/…,当您知道 SQL Hibernate 实际发送到您的数据库的内容时,通常可以最好地解决性能问题。
  • 我不是 SQLServer 专家,但如果device.userId 上存在索引并且数据库确实使用该索引,则代码没有理由花费那么长时间。我的猜测是非参数化查询使用了 Hibernate 使用的参数化查询不使用的缓存/索引。 Hibernate 不可能花费 200 毫秒。创建三个设备实例。所以要么是数据库是罪魁祸首,要么是sender.sendSms() 是那个花了这么多时间的人。

标签: java sql-server performance hibernate jpa


【解决方案1】:

当您运行 JPQL 查询时,结果本身不一定是从数据库中获取的唯一内容(您不一定要查询单个)。

如果您的Device 类引用了User(或多个),则在您查询Device 时也会加载User。此外,实例化对象并在EntityManager 中注册它们会产生一些开销(尽管我怀疑这个开销并没有那么大。)

本质上,您不能期望 JPQL 查询与 SQL 查询一样快,因为它们远非相同,即使它们在语法上相似。

如果您编写本机 sql 查询并通过 Hibernate 运行它,我预计它所花费的时间与直接运行 sql 查询一样多。

当涉及到延迟加载时,Hibernate 需要重新访问数据库以检索延迟加载的实体,这当然需要一些时间。您节省了加载拥有对象的时间,但是一旦您尝试加载延迟获取的关联,所节省的时间就会花费(尽管据我所见,急切获取所花费的时间与延迟获取然后延迟加载所花费的时间差不多,只是将两者分开几毫秒..)

如果您想查看 Hibernate 根据您的 JPQL 查询生成的 sql,请将 &lt;property name="hibernate.show_sql"&gt;true&lt;/property&gt; 添加到您的 Hibernate 配置中,并将以下内容添加到您的 log4j.properties:

log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.type=TRACE

【讨论】:

  • 这个笑话是一个本地 SQL 查询来获取用户的设备需要超过 1 秒才能完成。而从 SQLServer 控制台发出的相同查询需要不到 1 毫秒。也许,Hibernate 会为每个查询重新打开连接。
  • 然后我会检查我的 SQL 驱动程序。使用 jTDS 驱动程序,来自 Microsoft 的驱动程序有很多怪癖。
【解决方案2】:

问题在于 Java 以 Unicode 格式发送查询参数,而 SQLServer 也必须将所有列转换为 Unicode。在 persistence.xml 中将 sendStringParametersAsUnicode=false 参数设置为 connection 会有所帮助。 这段代码花费了 200 毫秒,现在只需 2-5 毫秒。 不幸的是,我花了 2 天时间才找到原因,正如我所想的那样,问题出在 Hibernate 中。 见:http://www.jochenhebbrecht.be/site/2014-05-01/java/fixing-slow-queries-running-sql-server-using-jpa-hibernate-and-jtds

【讨论】:

  • 这不是一个好的答案。可以回答他自己的问题,但答案应该提供解释,而不仅仅是指向其他地方的链接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-19
  • 2013-06-19
  • 2011-06-25
  • 1970-01-01
  • 2010-10-03
  • 1970-01-01
  • 2019-03-28
相关资源
最近更新 更多