【问题标题】:How is Hibernate deciding order of update/insert/deleteHibernate如何决定更新/插入/删除的顺序
【发布时间】:2012-09-27 07:35:26
【问题描述】:

让我们先忘记 Hibernate。假设我有两个表,A & B。两个事务正在更新这两个表中的相同记录,但是 txn 1 更新 B 然后 A,而 txn 2 更新 A 然后 B。这是一个典型的死锁示例。避免这种情况的最常见方法是预先定义获取资源的顺序。例如,我们应该先更新表 A,然后再更新 B。

返回休眠状态。当我们在一个会话中更新大量实体时,一旦我刷新会话,不同实体的更改将生成相应的插入/更新/删除语句到数据库。 Hibernate 是否有一些算法来决定实体之间的更新顺序?如果不是,Hibernate 用于防止第 1 段中描述的死锁情况的方式是什么?

如果 Hibernate 正在维护订单,我如何知道或控制订单?我不希望我在 DB 中的显式更新与 Hibernate 发生冲突,并导致死锁。

【问题讨论】:

    标签: hibernate deadlock database-deadlocks


    【解决方案1】:

    您描述的问题不是由数据库处理的,根据我的经验,Hibernate 也没有完全处理。

    您必须采取明确的步骤来避免它成为问题。

    Hibernate 会为您完成一些工作。根据前面的回答,Hibernate 确保在隔离刷新中,插入、删除和更新的排序方式可以确保它们以可实现的顺序应用。请参阅 AbstractFlushingEventListener 类中的 performExecutions(EventSource session)

    以特殊顺序执行所有 SQL(和二级缓存更新),以免违反外键约束:

    1. 按执行顺序插入
    2. 更新
    3. 集合元素的删除
    4. 集合元素的插入
    5. 按执行顺序删除

    当具有唯一约束时,了解此顺序非常重要,尤其是如果您想替换一对多子(删除旧的/插入新的)但旧的和新的子共享相同的唯一约束(例如相同的电子邮件地址)。在这种情况下,您可以更新旧条目,而不是删除/插入,或者您可以在删除后刷新,然后继续插入。更详细的例子可以查看this article

    请注意,它没有指定更新的顺序。检查 Hibernate 代码让我认为更新顺序将取决于将实体添加到持久性上下文中的顺序,NOT 更新它们的顺序。这在您的代码中可能是可以预测的,但阅读 Hibernate 代码并没有让我觉得我会依赖这种排序。

    我能想到三种解决方案:

    1. 尝试将hibernate.order_updates 设置为true。当同一个表中的多行被更新时,这应该有助于避免死锁,但对于跨多个表的死锁没有帮助。
    2. 在进行任何更新之前,让您的事务对其中一个实体进行PESSIMISTIC_WRITE 锁定。您使用哪个实体将取决于您的具体情况,但只要您确保在存在死锁风险的情况下始终选择实体,这将阻塞事务的其余部分,直到可以获得锁为止。
    3. 编写代码以在死锁发生时捕获并以合理的方式重试。管理死锁重试的组件必须位于当前事务边界之外。这是因为必须关闭失败的会话并回滚相关的事务。在this article 中,您可以找到自动重试 AOP 特性的示例。

    【讨论】:

    • 请接受我的道歉,由于未知原因,我之前错过了这个答案:) 我相信你在 Hibernate 代码中的检查结果实际上给出了迄今为止最可靠的答案。很好的建议,尤其是第 1 点和第 2 点
    【解决方案2】:

    关于第一个例子 - 这种东西由数据库处理(阅读更多关于事务隔离级别和数据库锁定策略的信息)。有很多不同的方法可以处理这个问题。

    至于 Hibernate,org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(EventSource) 的 javadoc 说:

    按特殊顺序执行所有 SQL 和二级缓存更新,以免违反外键约束:

    1. 按执行顺序插入
    2. 更新集合元素的删除
    3. 集合元素的插入
    4. 按执行顺序删除

    我假设这是 Hibernate 对已执行 SQL 查询进行的唯一优化。其余问题由数据库处理。

    【讨论】:

    • 我相信第一个例子的东西大多不能被数据库处理。对于隔离级别,据我了解,更多的是关于一个事务的更改如何对另一个事务可见。然而,即使对于最低的隔离级别,写入同一行的两个事务仍然会有锁定。因此,如果一个 txn 更新 A 记录然后 B,另一个 txn 更新 B 记录然后 A,它仍然会产生死锁。 DBMS 能做的最好的事情就是检测潜在的死锁发生。我知道你提到的 Hibernate SQL 执行顺序,但我关心的是关于
    • 实体之间的更新顺序。如果 Hibernate 没有对此进行任何处理并假设可以由 DB 处理(这显然是不现实的),那将是非常令人惊讶的
    • Oracle 例如以某种方式处理死锁“通过回滚与检测死锁的事务关联的语句自动检测并解决死锁”:oracle-base.com/articles/misc/deadlocks.php。这可能会导致 JDBC 异常,因此您可以在处理异常的代码中添加一些重试逻辑。
    • 当然,最好通过确保以适当的顺序获取资源来避免死锁。
    • 这就是我所说的“DBMS...检测潜在的死锁发生”的意思。但是,DBMS 并未广泛提供此功能,只能作为最后的手段使用。
    猜你喜欢
    • 1970-01-01
    • 2020-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-27
    相关资源
    最近更新 更多