【问题标题】:Spring Transaction Isolation LevelSpring事务隔离级别
【发布时间】:2015-07-21 13:25:30
【问题描述】:

我们大多数人可能都在使用 Spring 和 Hibernate 进行数据访问。 我试图了解 Spring Transaction Manager 的一些内部结构。

根据 Spring API,它支持不同的隔离级别 - doc 但我找不到明确的信息,这些信息在哪些情况下确实有助于提高性能。

我知道Spring Transaction 中的readOnly 参数可以帮助我们使用不同的TxManager 来读取只读数据,并且可以利用良好的性能。但它locks 表来获取数据以避免脏读/未提交的读取 - doc

假设,在少数情况下,我们可能想在不锁定表的情况下盲目地将记录插入表中并检索信息,在这种情况下,我们从不更新表数据,我们只是插入和读取 [append-only]。我们可以使用更好的隔离来获得任何性能吗?

  1. 正如您从其中一个参考链接中看到的那样,我们真的需要实现/编写我们自己的 CustomJPADiaelect 吗?
  2. 对于我的要求,哪种隔离更好?

【问题讨论】:

    标签: java mysql spring hibernate transactions


    【解决方案1】:

    Read-only 允许某些优化,例如禁用脏检查,当您不打算更改实体时,您应该完全使用它。

    每个isolation level 定义了数据库必须施加多少锁定以确保防止数据异常。

    大多数数据库使用MVCC(Oracle、PostgreSQL、MySQL),因此读取器不会锁定写入器并且写入器不会锁定读取器。如您在the following example 中所见,只有writers 锁定writers

    REPEATABLE_READ 不必持有 来防止并发事务修改您当前加载的事务行。 MVCC 引擎允许其他事务读取行的已提交状态,即使您当前的事务已更改它但尚未提交(MVCC 使用撤消日志来恢复 pending changed 行的先前版本)。

    在您的用例中,您应该使用READ_COMMITTED,因为它比其他更严格的隔离级别具有更好的扩展性,并且您应该使用optimistic locking 来防止在长时间对话中丢失更新。


    更新

    @Transactional(isolation = Isolation.SERIALIZABLE) 设置为 Spring bean 具有不同的行为,具体取决于当前事务类型:

    • 对于RESOURCE_LOCAL 事务,JpaTransactionManager 可以为当前运行的事务应用特定的隔离级别。
    • 对于 JTA 资源,事务范围的隔离级别不会传播到底层数据库连接,因为这是默认的 JTA 事务管理器行为。您可以按照WebLogicJtaTransactionManager 的示例覆盖它。

    【讨论】:

    • 在这篇文章之前我也浏览过你的博客。但是我们真的需要实现我们自己的 JPADialect 来实现它吗?
    • 你不应该实现 JPADialect,你为什么这么认为?
    • 其中一个参考链接指向这一点。默认情况下不支持它:-/我发现很难相信
    • 把链接发给我,我不明白你指的是什么。
    • 对于 JPA,隔离级别正确地取自 @Transaction。如果您使用 JTA,则需要一些解决方法,因为 JTA 标准不支持它。
    【解决方案2】:

    实际上readOnly=true不会对数据库表造成任何锁争用,因为根本不需要锁定——数据库能够恢复到以前版本的记录而忽略所有新的更改。

    如果 readOnly 为 true,您将在当前 Hibernate 会话中将刷新模式设置为 FlushMode.NEVER,从而阻止会话提交事务。另外,在JDBC Connection上会调用setReadOnly(true),这也是对底层数据库不提交更改的提示。

    所以readOnly=true 正是您正在寻找的(例如SERIALIZED 隔离级别)。

    Here 是一个很好的解释。

    【讨论】:

    • 我可能同意readOnly 部分。 writes 呢?我们可以在这种情况下使用任何特定的隔离级别吗?