java - 如何避免两个不同的线程从数据库中读取相同的行(Hibernate 和 Oracle 10g)

标签 java oracle hibernate transactions locking

假设我有两个不同的线程,T1 和 T2,同时访问同一个数据库并从同一个表中获取数据。

现在在线程启动时,我需要从表中获取数据并将行存储到一个集合中,然后我将使用它在其他地方执行一些工作。我不希望两个线程能够处理相同的数据,因为这会导致重复(和长时间)的工作。更具体地说,这是一个企业应用程序,需要在启动时加载一些记录并将其存储在一个集合中以执行一些额外的工作。问题在于,在集群环境中,这可能会导致两个不同的实例加载相同的数据,因此可能会重复工作。所以我希望行仅由单个实例加载一次。

我怎样才能避免这种情况?

我目前正在使用 Hibernate 和 Oracle 10g。这些是我到目前为止的解决方案:

  • 以编程方式锁定行。第一个读取它的线程将某些“锁定”列设置为 true,但如果第一个线程在未将行设置为“已处理”的情况下死亡,则很可能发生死锁。

  • 使用悲观锁定。我尝试使用 LockMode.UPGRADE 但这似乎没有帮助,因为我仍然能够同时从两个线程读取数据。

public List<MyObject> getAllNtfFromDb() {
      Session session = HibernateUtil.getOraclesessionfactory().openSession();
      Query q = session.createQuery(
              "from MyObject n where n.state = 'NEW'");
    List<MyObject> list = (List<MyObject>) q.list();
      for (int i=0; i<list.size(); i++)
          session.lock(list.get(i), LockMode.UPGRADE);
return list;
}

还有其他提示吗?我究竟做错了什么?

谢谢。

最佳答案

您需要在查询时使用PESSIMISTIC_WRITE:

Query q = session
    .createQuery("from MyObject n where n.state = 'NEW'")
    .setLockOptions(new LockOptions(LockMode.PESSIMISTIC_WRITE));
List<MyObject> list = (List<MyObject>) q.list();

锁定父对象就足够了。死锁不一定会发生。如果持有锁的线程没有在另一个线程等待超时之前释放它,您可能会获得锁获取失败。

因为您使用的是 Oracle,所以这就是 SELECT FOR UPDATE作品:

SELECT ... FOR UPDATE locks the rows and any associated index entries, the same as if you issued an UPDATE statement for those rows. Other transactions are blocked from updating those rows, from doing SELECT ... LOCK IN SHARE MODE, or from reading the data in certain transaction isolation levels. Consistent reads ignore any locks set on the records that exist in the read view. (Old versions of a record cannot be locked; they are reconstructed by applying undo logs on an in-memory copy of the record.)

因此,如果 T1 获得了对某些行的独占锁,T2 将无法读取这些记录,直到 T1 提交或回滚。如果 T2 使用 READ_UNCOMMITTED隔离级别,则 T2 永远不会阻塞锁定记录,因为它只是使用撤消日志来重建数据,就像查询开始时一样。与 SQL 标准相反,Oracle READ_UNCOMMITTED 将:

To provide a consistent, or correct, answer, Oracle Database will create a copy of the block containing this row as it existed when the query began ... Effectively, Oracle Database takes a detour around the modified data—it reads around it, reconstructing it from the undo (also known as a rollback ) segment. A consistent and correct answer comes back without waiting for the transaction to commit.

关于java - 如何避免两个不同的线程从数据库中读取相同的行(Hibernate 和 Oracle 10g),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27968877/

相关文章:

java - 保存 Sprite 位置

java - 不指定数据库的JDBC MYSQL连接?

java - Hibernate 原生查询 - char(3) 列

java - Hibernate JPA 级联类型

java - @OneToMany 关联的标准

java - 如何获得准备好嵌入的 Mac JRE(在 Linux 上)?

JAVA Trivia 游戏数组超出范围

sql - 在CTE中建立定义值的列表

oracle - 脚本中包含一个远程表的 DRIVING_SITE Oracle 提示

java - @EmbeddedId 和使用 @GenerateValue 自动生成的 id,这种组合不能按需要工作