【问题标题】:How to avoid two different threads read the same rows from DB (Hibernate and Oracle 10g)如何避免两个不同的线程从 DB 中读取相同的行(Hibernate 和 Oracle 10g)
【发布时间】:2015-03-14 04:01:21
【问题描述】:

假设我有两个不同的线程,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;
}

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

谢谢。

【问题讨论】:

  • 也许您可以使用 select for update 语句?
  • 您能否提供用于此行的 Hibernate 导入:Session session = HibernateUtil.getOraclesessionfactory().openSession();

标签: java oracle hibernate transactions locking


【解决方案1】:

查询时需要使用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 锁定行和任何关联的索引 条目,就像您为这些行发出 UPDATE 语句一样。 其他事务被阻止更新这些行,做 SELECT ... LOCK IN SHARE MODE,或从特定的读取数据 事务隔离级别。一致读取忽略任何设置的锁 存在于读取视图中的记录。 (旧版唱片 无法锁定;它们是通过在 记录的内存副本。)

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

为了提供一致或正确的答案,Oracle 数据库将 创建包含该行的块的副本,因为它存在于 查询开始...实际上,Oracle 数据库绕着 修改后的数据——它读取它,从撤销中重建它 (也称为回滚)段。一致且正确的答案 无需等待事务提交即可返回。

【讨论】:

  • 好的,谢谢,这确实有效,但前提是两个线程试图在非常接近的时间内读取相同的数据。如果 T1 读取数据并在 1 分钟后 T2 读取相同的数据(T1 很可能仍在处理),则 T2 能够看到行。我想锁定该行,直到它们被更新(例如,状态列设置为“OK”或“PROCESSED”)。另外,更新一个实体会自动释放该行吗?如果会话关闭或线程崩溃,是否会在超时后释放行?非常感谢。
  • 可能依赖底层数据库独占锁支持。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多