Multithreading 处理长时间运行的Hibernate事务中的死锁

Multithreading 处理长时间运行的Hibernate事务中的死锁,multithreading,hibernate,transactions,deadlock,Multithreading,Hibernate,Transactions,Deadlock,我有一个Hibernate应用程序,它可以(通过Session.saveOrUpdate)对具有相同主键的记录进行并发插入和更新,主键是分配的。这些事务运行时间较长,平均可能为15秒(因为数据是从远程源收集的,并在数据进入时持久化)。我的DB隔离级别设置为Read Committed,我使用的是MySQL和InnoDB 问题是,此场景会创建过多的锁等待超时,这可能是由于死锁或长事务造成的。这就引出了几个问题: 数据库引擎是否仅在提交事务时释放其锁 如果是这样,我是否应该缩短我的交易时间 如果是

我有一个Hibernate应用程序,它可以(通过
Session.saveOrUpdate
)对具有相同主键的记录进行并发插入和更新,主键是分配的。这些事务运行时间较长,平均可能为15秒(因为数据是从远程源收集的,并在数据进入时持久化)。我的DB隔离级别设置为Read Committed,我使用的是MySQL和InnoDB

问题是,此场景会创建过多的锁等待超时,这可能是由于死锁或长事务造成的。这就引出了几个问题:

  • 数据库引擎是否仅在提交事务时释放其锁
  • 如果是这样,我是否应该缩短我的交易时间
  • 如果是这样,使用单独的读和写事务是否是一种好的做法,在这种情况下,写事务可以缩短,并且只能在收集了我的所有数据之后发生(我的大部分事务长度涉及收集远程数据)
编辑:

这里有一个简单的测试,它近似于我认为正在发生的事情。因为我处理的是长时间运行的事务,所以提交在第一次刷新之后很久才会发生。因此,为了说明我的情况,我将commit排除在测试之外:

@Entity
static class Person {
    @Id
    Long id = Long.valueOf(1);
    @Version
    private int version;
}

@Test
public void updateTest() {
    for (int i = 0; i < 5; i++) {
        new Thread() {
            public void run() {
                Session s = sf.openSession();
                Transaction t = s.beginTransaction();
                Person p = new Person();
                s.saveOrUpdate(p);
                s.flush(); // Waits...
            }
        }.run();
    }
}

没错,只有在提交事务时,数据库才会释放锁。因为您使用的是hibernate,所以可以使用乐观锁定,它会长时间锁定数据库。本质上,hibernate按照您的建议执行,将读写部分分离为单独的事务。写入时,它检查内存中的数据在数据库中是否没有同时更改

机会主义锁定: 基本假设:更新冲突很少发生。 技工:

  • 读取具有版本字段的数据集

  • 更改数据集

  • 更新数据集 3.1.使用当前版本字段和键读取数据集 如果你知道了,没有人改变记录。 应用下一版本字段值。 更新记录。 如果您没有收到,则记录已被更改,请向调用者返回一条优先消息,这样您就完成了

  • 插入也不受影响 还是要有一个单独的主键 或者您接受具有相同值的多条记录。 因此,上面给出的示例不是乐观锁定的情况


  • 据我所知,使用Hibernate启用乐观锁定只需要在实体中的int上抛出@Version注释。我确实尝试过上面描述的场景,但没有任何好处。请参阅我在原始帖子中添加的测试,该测试使用乐观版本控制来重现场景。我讨厌在5年后遇到完全相同的问题时遇到这些类型的问题,我看不到答案:(
    select id, version from person where id=?
    insert into person (version, id) values (?, ?)
    select id, version from person where id=?
    insert into person (version, id) values (?, ?)