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

How to avoid two different threads read the same rows from DB (Hibernate and Oracle 10g)

假设我有两个不同的线程,T1 和 T2,它们同时访问同一个数据库并从同一个 table.

中获取数据

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

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

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

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.