Hibernate真的很慢。如何让它更快?

时间:2016-03-16 20:59:43

标签: java performance hibernate

在我的应用中。我有Case,每个Case可以有0到2 Claim。如果Case有0个声明它运行得非常快,1声称它会减慢速度,而2则非常慢。知道如何加快速度吗?我不知道我的案件和索赔是否来回导致无限重现,所以我添加了一个JsonManagedReference和JsonBackReference,但这似乎对速度没什么帮助。有任何想法吗?这是我的Case.java:

@Entity
public class Case {
    @OneToMany(mappedBy="_case", fetch = FetchType.EAGER)
    @Fetch(FetchMode.JOIN)
    @JsonManagedReference(value = "case-claim")
    public Set<Claim> claims;
}

在Claim.java中:

@Entity
public class Claim implements Cloneable {

    @ManyToOne(optional = true)
    @JoinColumn(name = "CASE_ID")
    @JsonBackReference(value = "case-claim")
    private Case _case;
}

输出0索赔: https://gist.github.com/elmatt/2cafbe7ecb1fa0b7f6a8

2项索赔的输出: https://gist.github.com/elmatt/b000bc28909453effc95

2 个答案:

答案 0 :(得分:1)

您的问题与Case和Claim之间的关系无关。

仅供参考:300毫秒并非“非常快”。您的问题是,您希望hibernate能够神奇地快速地为您提供复杂的对象层次结构,而您无需付出特别的努力。我认为ORM是“大谎言” - 它非常易于使用并且在玩具问题上非常有效,但是当你试图扩展到有趣的应用程序(如你的)时往往会失败。

不要放弃休眠,但要意识到你需要比你想象的更努力,才能使它适合你。

我碰巧在类似的数据域工作(判决后医疗索赔分析和处理)。您应该能够在每个索赔10毫秒(包括所有相关维度)的情况下,使用基于适度硬件的MySQL从具有> 10亿个声明的表中选择此类数据,并将数据库托管在与应用程序不同的服务器上。

你如何从你所处的位置到达你应该去的地方? 1.通过最小化执行的单独查询的数量,最小化到数据库的往返次数。 2.手工制作您的重要查询,以获取您实际需要的行和连接。 3.对每个查询使用explain plan以确保它以正确的顺序命中表,并且索引适当地支持每个步骤。 4.考虑对大表进行分区并在查询中包含分区条件,以使分区修剪能够将查询集中在正确的数据上。 5.让hibernate管理你的实体之间的关系是非常犹豫的。我一般不会让hibernate处理任何关系。

几年前,我开发了一款iPhone应用程序产品,用户可以通过工作流程(例如护士服用患者的生命体征),并且每个屏幕都会向应用服务器进行往返,以执行工作流程步骤并获取下一个屏幕的数据。想想你可以在iPhone屏幕上使用的数据量很少。然而,往返的DB部分通常需要2-5秒来执行。那里的每个人都认为这是理所当然的,因为“这就是它总是花了多长时间。”我挖掘了代码,发现每一步都在拉动数据库的很大一部分(然后业务逻辑没有使用它)。

他们调整默认hibernate行为的唯一一次是因为连接太多而导致异常(是的,MySQL在一个查询中有67个表的限制)。

创建Java数据模型并简单地将其ORM到数据库中的方法通常可以很好地处理配置数据等,但往往对涉及事务数据的复杂数据模型执行起来非常糟糕。这就是你现在正在咬你的东西。

你的问题完全可以修复,并且可以逐步攻击 - 你不必拆开整个应用程序就可以开始做得更好了。

答案 1 :(得分:0)

您可以启用hibernate日志记录并提供输出。它应该指示针对您的数据库执行的SQL查询。有关您使用的DB的信息也很有用。当你有那些我建议分析查询,以确保您的数据库设置适当。这听起来像是一个非索引查询。

数据集的大小也有助于定位可能出现的问题 - 行数等等。

我还建议对实际的hibernate调用进行计时(可能与之前/之后的log语句一样粗略)与整体处理进行计时,以确定它是否真的是休眠或其他处理。没有进一步的信息和这里不明确的背景。

现在您已经发布了我们的查询,我们可以看到发生了什么。看起来您的实体结构比最初发布的代码片段更复杂。那里有人,活动,健康计划等的参考。

正如其他人所评论的那样,由于模型的性质,您的查询会触发大量数据的大量选择。

我建议为声明创建命名查询,然后使用Case的ID加载那些。

您还应该检查您的hibernate模型并切换到FetchType.LAZY,其他hibernate将创建大型查询,例如您发布的查询。这里的问题是,如果您尝试访问事务之外的相关实体,您将获得lazyinitializationexception。您需要考虑每个用例并确保加载所需的数据。 Hibernate的两个常见错误是在任何地方使用FetchType.EAGER或尽早启动事务以避免这种情况。没有一种正确的设计方法,但我通常会做以下

JSP - &gt;控制器 - &gt; [TX BOUNDARY]服务 - &gt; DAO

在将服务方法传递回控制器之前,您应该封装所需的业务逻辑来加载所需的业务逻辑。

同样,根据另一个答案,我认为你期待太多的Hibernate。它是一个功能强大的工具,但您需要了解它是如何工作的,以便从中获得最佳效果。