【问题标题】:What causes a committed update in Hibernate to be NOT visible to other threads?是什么导致 Hibernate 中的已提交更新对其他线程不可见?
【发布时间】:2021-02-19 06:30:49
【问题描述】:

问题

有一个 Quartz 作业,我将其称为 Job-A,它以固定间隔运行。在每个时间间隔,作业都在执行一些数据库更新的线程中运行。但是,下一次立即运行看不到上一次运行所做的更新。流程是这样的:

  1. Job-A 运行。主题:thread-1
    • 选择要处理的实体。
    • 在当前会话中提交连接,以使自上次提交/回滚以来所做的所有更改永久化,并释放当前持有的所有数据库锁。
    • 将实体(此处称为Entity-A)标记为processed。目标是只处理此实体一次
    • 更新并提交事务。
    • 刷新实体,使更改在后续操作中可见。
  2. Job-A 再次运行。主题:thread-2
    • 选择要处理的实体。
      • 预期: Thread-2Entity-A 视为 processed跳过它。
      • 实际:Thread-2 确实看到Entity-Aprocessed 状态并且再次选择它为什么?
    • 其余步骤相同。

技术栈

  1. 休眠 3.6.6
    • 会话上下文是ThreadLocal
  2. MySQL 5.7.3
  3. 石英 2.3.2
    • Job-A 带有 @DisallowConcurrentExecution 注释,因此多个 Job-A 实例永远不会同时运行。
    • Job-A 以秒为单位的固定间隔运行。

问题

  1. 我怀疑存在竞态条件,但竞态条件是否与此问题相关?如果是这样,我该如何缩小罪魁祸首?
  2. 其他可能的原因是什么?我该如何追踪?

【问题讨论】:

  • 如何获取当前会话? IIRC,休眠可以重用来自 ThreadLocal 存储的相同会话。但如果你想看到新的数据,你必须开始一个新的会话,我想。
  • @GreyFairer,我通过SessionFactorygetCurrentSession() 方法获取当前会话。在获得一个新的会话实例(如果没有重用)后,我开始一个事务。是的,Hibernate 可以从 ThreadLocal 重用相同的会话,因为打开和关闭会话的开销很大。
  • 打开/关闭会话并不昂贵,理想情况下应该为每个事务完成。

标签: java mysql multithreading hibernate quartz


【解决方案1】:

MySQL 的默认隔离级别是快照隔离。可能这就是原因。尝试通过 JDBC 驱动配置将隔离设置为读取提交。

【讨论】:

  • 这两个作业没有同时运行,所以快照隔离级别应该可以正常工作。但是 2 作业必须在执行开始时开始一个新事务,这似乎是出了问题。
  • 开始的事务在作业结束时提交。
  • 您是否尝试过已提交的读取解决了问题?
【解决方案2】:

由于您将SessionFactory.getCurrentSession() 与 ThreadLocal SessionContext 一起使用,您将拥有多个会话,并且每个会话都会将持久对象保存在内存中。

Quartz 使用线程池,因此它将为不同的作业重用线程。如果池中有超过 1 个线程,则您将拥有多个会话。

所以这可能发生:

  • 在线程 1 上运行 1:加载实体 A 但不更改它
  • 在线程 2 上运行 2:加载实体 A 并对其进行更新
  • 在线程 1 上运行 3:重用运行 1 中的会话并重用会话内存中未更改的实体 A

Hibernate 具有能够在没有事务管理器的情况下运行的传统,但在这种情况下,它会导致意想不到的结果。

最好让每个事务在单独的会话中运行。

try(Session session = SessionFactory.openSession()){
  // start transaction
  //...
  // commit transaction
}//auto-closes session

如果你使用 JTA 事务管理器,你也可以使用

hibernate.current_session_context_class = jta

这样,您可以继续使用SessionFactory.getCurrentSession(),但它将在每个事务中打开/关闭,这是最干净的解决方案。

不幸的是,我没有明确的示例如何为您的堆栈配置 JTA。

另请参阅:https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#architecture-current-session

如果您担心性能,最好配置二级缓存。二级缓存在会话之间共享,并且也是事务感知的,因此它将仅包含对象的最新提交版本。

另请参阅: https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#caching-config

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-10
    • 1970-01-01
    • 1970-01-01
    • 2014-08-21
    相关资源
    最近更新 更多